summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--PREUPLOAD.cfg1
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java11
-rw-r--r--core/api/current.txt35
-rw-r--r--core/api/module-lib-current.txt4
-rw-r--r--core/api/system-current.txt200
-rw-r--r--core/api/system-lint-baseline.txt17
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/app/ActivityThread.java5
-rw-r--r--core/java/android/app/ApplicationExitInfo.java2
-rw-r--r--core/java/android/app/ApplicationPackageManager.java22
-rw-r--r--core/java/android/app/PendingIntent.java2
-rw-r--r--core/java/android/app/SystemServiceRegistry.java73
-rw-r--r--core/java/android/app/TaskInfo.java20
-rw-r--r--core/java/android/app/assist/AssistStructure.java2
-rw-r--r--core/java/android/app/search/Query.java3
-rw-r--r--core/java/android/app/search/SearchAction.java5
-rw-r--r--core/java/android/app/search/SearchContext.java4
-rw-r--r--core/java/android/app/search/SearchTarget.java4
-rw-r--r--core/java/android/app/smartspace/ISmartspaceCallback.aidl27
-rw-r--r--core/java/android/app/smartspace/ISmartspaceManager.aidl45
-rw-r--r--core/java/android/app/smartspace/SmartspaceAction.java353
-rw-r--r--core/java/android/app/smartspace/SmartspaceConfig.aidl19
-rw-r--r--core/java/android/app/smartspace/SmartspaceConfig.java202
-rw-r--r--core/java/android/app/smartspace/SmartspaceManager.java64
-rw-r--r--core/java/android/app/smartspace/SmartspaceSession.java284
-rw-r--r--core/java/android/app/smartspace/SmartspaceSessionId.aidl19
-rw-r--r--core/java/android/app/smartspace/SmartspaceSessionId.java114
-rw-r--r--core/java/android/app/smartspace/SmartspaceTarget.aidl19
-rw-r--r--core/java/android/app/smartspace/SmartspaceTarget.java660
-rw-r--r--core/java/android/app/smartspace/SmartspaceTargetEvent.aidl19
-rw-r--r--core/java/android/app/smartspace/SmartspaceTargetEvent.java204
-rw-r--r--core/java/android/appwidget/AppWidgetProviderInfo.java8
-rw-r--r--core/java/android/content/Context.java13
-rw-r--r--core/java/android/content/pm/LauncherActivityInfo.java27
-rw-r--r--core/java/android/content/pm/PackageInfo.java2
-rw-r--r--core/java/android/content/pm/PackageManager.java30
-rw-r--r--core/java/android/content/pm/Signature.java4
-rw-r--r--core/java/android/content/res/CompatibilityInfo.java31
-rw-r--r--core/java/android/hardware/Battery.java74
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl4
-rw-r--r--core/java/android/hardware/input/InputDeviceBattery.java65
-rw-r--r--core/java/android/hardware/input/InputManager.java35
-rw-r--r--core/java/android/hardware/location/ContextHubClientCallback.java31
-rw-r--r--core/java/android/hardware/location/ContextHubIntentEvent.java45
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java128
-rw-r--r--core/java/android/hardware/location/IContextHubClientCallback.aidl3
-rw-r--r--core/java/android/hardware/location/IContextHubService.aidl6
-rw-r--r--core/java/android/hardware/location/NanoAppState.java24
-rw-r--r--core/java/android/net/IpSecManager.java91
-rw-r--r--core/java/android/net/http/SslCertificate.java2
-rw-r--r--core/java/android/os/CombinedVibrationEffect.java29
-rw-r--r--core/java/android/os/IVibratorManagerService.aidl4
-rw-r--r--core/java/android/os/PowerManager.java80
-rw-r--r--core/java/android/os/strictmode/IncorrectContextUseViolation.java13
-rw-r--r--core/java/android/provider/Settings.java18
-rw-r--r--core/java/android/service/autofill/Dataset.java4
-rw-r--r--core/java/android/service/notification/INotificationListener.aidl2
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java25
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java3
-rw-r--r--core/java/android/service/smartspace/ISmartspaceService.aidl46
-rw-r--r--core/java/android/service/smartspace/SmartspaceService.java313
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java3
-rw-r--r--core/java/android/util/imetracing/ImeTracing.java6
-rw-r--r--core/java/android/util/imetracing/OWNERS3
-rw-r--r--core/java/android/view/ContentInfo.java4
-rw-r--r--core/java/android/view/InputDevice.java35
-rw-r--r--core/java/android/view/InsetsState.java33
-rw-r--r--core/java/android/view/SurfaceControl.java35
-rw-r--r--core/java/android/view/View.java14
-rw-r--r--core/java/android/view/ViewStructure.java4
-rw-r--r--core/java/android/view/WindowManager.java41
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java2
-rw-r--r--core/java/android/view/textservice/TextServicesManager.java4
-rw-r--r--core/java/android/widget/AnalogClock.java114
-rw-r--r--core/java/android/widget/SpellChecker.java58
-rw-r--r--core/java/android/window/TaskOrganizer.java3
-rw-r--r--core/java/com/android/internal/app/OWNERS1
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java5
-rw-r--r--core/java/com/android/internal/os/BatteryUsageStatsProvider.java11
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl7
-rw-r--r--core/jni/android_view_InputDevice.cpp10
-rw-r--r--core/jni/android_view_SurfaceControl.cpp20
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp16
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--core/res/res/values/attrs.xml1
-rw-r--r--core/res/res/values/config.xml9
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/AndroidManifest.xml5
-rw-r--r--core/tests/coretests/src/android/app/activity/BroadcastTest.java39
-rw-r--r--core/tests/coretests/src/android/app/activity/LaunchpadActivity.java40
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java2
-rw-r--r--core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java2
-rw-r--r--core/tests/coretests/src/android/content/pm/SignatureTest.java26
-rw-r--r--core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java2
-rw-r--r--core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java55
-rw-r--r--graphics/java/android/graphics/RenderNode.java33
-rw-r--r--keystore/java/android/security/Credentials.java6
-rw-r--r--keystore/java/android/security/KeyStore.java4
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java36
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java5
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt208
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt98
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt102
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt101
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt91
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt95
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt153
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt93
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncherTest.kt)96
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt99
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt100
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt92
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt100
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt92
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt)106
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreenTest.kt)4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt95
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt95
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt150
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt100
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt105
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt163
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt74
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/RecordingCanvas.cpp2
-rw-r--r--libs/hwui/RenderProperties.h6
-rw-r--r--libs/hwui/effects/StretchEffect.cpp27
-rw-r--r--libs/hwui/effects/StretchEffect.h66
-rw-r--r--libs/hwui/jni/android_graphics_RenderNode.cpp27
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.cpp2
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp20
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp8
-rw-r--r--location/java/android/location/GnssMeasurement.java6
-rw-r--r--location/java/android/location/LocationManager.java1
-rw-r--r--location/java/android/location/provider/LocationProviderBase.java4
-rw-r--r--media/java/android/media/musicrecognition/MusicRecognitionManager.java5
-rw-r--r--media/java/android/media/musicrecognition/MusicRecognitionService.java5
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java2
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java16
-rw-r--r--media/java/android/media/tv/tuner/filter/RestartEvent.java24
-rw-r--r--media/jni/android_media_MediaMetricsJNI.cpp8
-rw-r--r--native/graphics/jni/Android.bp3
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java83
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityManager.java22
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkCapabilities.java21
-rw-r--r--packages/Connectivity/service/Android.bp1
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_off.xml18
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_on.xml18
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/drawable/track_off.xml2
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/drawable/track_off_background.xml12
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/drawable/track_on.xml2
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/drawable/track_on_background.xml13
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml4
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml1
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/values/colors.xml1
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml6
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/values/styles.xml1
-rw-r--r--packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java6
-rw-r--r--packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java28
-rw-r--r--packages/SettingsLib/UsageProgressBarPreference/Android.bp1
-rw-r--r--packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml34
-rw-r--r--packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java27
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java30
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java2
-rw-r--r--packages/SystemUI/res/values/flags.xml5
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java528
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java5
-rw-r--r--proto/src/system_messages.proto4
-rw-r--r--services/Android.bp2
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java6
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java19
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java19
-rw-r--r--services/core/java/com/android/server/TestNetworkService.java6
-rw-r--r--services/core/java/com/android/server/VibratorManagerService.java482
-rw-r--r--services/core/java/com/android/server/Watchdog.java1
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java24
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java16
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java10
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessProfileRecord.java2
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java3
-rw-r--r--services/core/java/com/android/server/graphics/fonts/FontCrashDetector.java92
-rw-r--r--services/core/java/com/android/server/graphics/fonts/FontManagerService.java53
-rw-r--r--services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java4
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java14
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java142
-rw-r--r--services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java10
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java13
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java47
-rw-r--r--services/core/java/com/android/server/notification/ValidateNotificationPeople.java47
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java4
-rw-r--r--services/core/java/com/android/server/pm/DynamicCodeLoggingService.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java28
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java9
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java1
-rw-r--r--services/core/java/com/android/server/vcn/Vcn.java13
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java12
-rw-r--r--services/core/java/com/android/server/vcn/VcnNetworkProvider.java20
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java5
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java7
-rw-r--r--services/core/java/com/android/server/wm/BarController.java70
-rw-r--r--services/core/java/com/android/server/wm/CompatModePackages.java86
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java17
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java49
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java104
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java14
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java42
-rw-r--r--services/core/java/com/android/server/wm/Task.java28
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java33
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp25
-rw-r--r--services/java/com/android/server/SystemServer.java8
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java7
-rw-r--r--services/smartspace/Android.bp13
-rw-r--r--services/smartspace/java/com/android/server/smartspace/RemoteSmartspaceService.java112
-rw-r--r--services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java184
-rw-r--r--services/smartspace/java/com/android/server/smartspace/SmartspaceManagerServiceShellCommand.java84
-rw-r--r--services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java411
-rw-r--r--services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java468
-rw-r--r--services/tests/servicestests/src/com/android/server/VibratorServiceTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/graphics/fonts/FontCrashDetectorTest.java77
-rw-r--r--services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java115
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java80
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java51
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java55
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java50
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java62
-rw-r--r--telephony/java/android/telephony/ims/ImsCallProfile.java4
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl10
-rw-r--r--tests/FlickerTests/TEST_MAPPING13
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt49
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt3
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java44
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java40
-rw-r--r--tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java18
-rw-r--r--tests/net/Android.bp1
-rw-r--r--tests/net/java/android/net/Ikev2VpnProfileTest.java2
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java44
-rw-r--r--wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java6
279 files changed, 9180 insertions, 2599 deletions
diff --git a/Android.bp b/Android.bp
index d170913150c5..dc92586a8d14 100644
--- a/Android.bp
+++ b/Android.bp
@@ -632,6 +632,7 @@ java_defaults {
],
sdk_version: "core_platform",
static_libs: [
+ "bouncycastle-repackaged-unbundled",
"framework-internal-utils",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
// in favor of an API stubs dependency in java_library "framework" below.
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index cdf5df6c6bd3..30ed7de92614 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,6 +8,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
cmds/input/
cmds/uinput/
core/jni/
+ libs/hwui/
libs/input/
native/
services/core/jni/
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index e05f0b062dbe..223f3b04d4e6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -665,9 +665,18 @@ class JobConcurrencyManager {
WorkTypeConfig(@NonNull String configIdentifier, int defaultMaxTotal,
List<Pair<Integer, Integer>> defaultMin, List<Pair<Integer, Integer>> defaultMax) {
mConfigIdentifier = configIdentifier;
- mDefaultMaxTotal = mMaxTotal = defaultMaxTotal;
+ mDefaultMaxTotal = mMaxTotal = Math.min(defaultMaxTotal, MAX_JOB_CONTEXTS_COUNT);
+ int numReserved = 0;
for (int i = defaultMin.size() - 1; i >= 0; --i) {
mDefaultMinReservedSlots.put(defaultMin.get(i).first, defaultMin.get(i).second);
+ numReserved += defaultMin.get(i).second;
+ }
+ if (mDefaultMaxTotal < 0 || numReserved > mDefaultMaxTotal) {
+ // We only create new configs on boot, so this should trigger during development
+ // (before the code gets checked in), so this makes sure the hard-coded defaults
+ // make sense. DeviceConfig values will be handled gracefully in update().
+ throw new IllegalArgumentException("Invalid default config: t=" + defaultMaxTotal
+ + " min=" + defaultMin + " max=" + defaultMax);
}
for (int i = defaultMax.size() - 1; i >= 0; --i) {
mDefaultMaxAllowedSlots.put(defaultMax.get(i).first, defaultMax.get(i).second);
diff --git a/core/api/current.txt b/core/api/current.txt
index ec7fcb48f7ce..3efb78943bd7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -722,6 +722,7 @@ package android {
field public static final int gwpAsanMode = 16844310; // 0x1010616
field public static final int hand_hour = 16843011; // 0x1010103
field public static final int hand_minute = 16843012; // 0x1010104
+ field public static final int hand_second = 16844323; // 0x1010623
field public static final int handle = 16843354; // 0x101025a
field public static final int handleProfiling = 16842786; // 0x1010022
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
@@ -8350,6 +8351,7 @@ package android.appwidget {
method public android.appwidget.AppWidgetProviderInfo clone();
method public int describeContents();
method public final android.os.UserHandle getProfile();
+ method @NonNull public android.content.pm.ActivityInfo getProviderInfo();
method @Nullable public final String loadDescription(@NonNull android.content.Context);
method public final android.graphics.drawable.Drawable loadIcon(@NonNull android.content.Context, int);
method public final String loadLabel(android.content.pm.PackageManager);
@@ -11868,6 +11870,7 @@ package android.content.pm {
}
public class LauncherActivityInfo {
+ method @NonNull public android.content.pm.ActivityInfo getActivityInfo();
method public android.content.pm.ApplicationInfo getApplicationInfo();
method public android.graphics.drawable.Drawable getBadgedIcon(int);
method public android.content.ComponentName getComponentName();
@@ -12240,6 +12243,7 @@ package android.content.pm {
method @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForActivity(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.res.Resources getResourcesForApplication(@NonNull android.content.pm.ApplicationInfo, @Nullable android.content.res.Configuration) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
@@ -12468,6 +12472,7 @@ package android.content.pm {
field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
field public static final int PERMISSION_DENIED = -1; // 0xffffffff
field public static final int PERMISSION_GRANTED = 0; // 0x0
+ field public static final String PROPERTY_MEDIA_CAPABILITIES = "android.media.PROPERTY_MEDIA_CAPABILITIES";
field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
field public static final int SIGNATURE_MATCH = 0; // 0x0
field public static final int SIGNATURE_NEITHER_SIGNED = 1; // 0x1
@@ -16800,6 +16805,18 @@ package android.graphics.text {
package android.hardware {
+ public abstract class Battery {
+ ctor public Battery();
+ method @FloatRange(from=-1.0F, to=1.0f) public abstract float getCapacity();
+ method public abstract int getStatus();
+ method public abstract boolean hasBattery();
+ field public static final int STATUS_CHARGING = 2; // 0x2
+ field public static final int STATUS_DISCHARGING = 3; // 0x3
+ field public static final int STATUS_FULL = 5; // 0x5
+ field public static final int STATUS_NOT_CHARGING = 4; // 0x4
+ field public static final int STATUS_UNKNOWN = 1; // 0x1
+ }
+
@Deprecated public class Camera {
method @Deprecated public final void addCallbackBuffer(byte[]);
method @Deprecated public final void autoFocus(android.hardware.Camera.AutoFocusCallback);
@@ -25632,8 +25649,6 @@ package android.net {
method public void applyTransportModeTransform(@NonNull java.net.Socket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.net.DatagramSocket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.io.FileDescriptor, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @NonNull @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public void removeTransportModeTransforms(@NonNull java.net.Socket) throws java.io.IOException;
@@ -25643,12 +25658,6 @@ package android.net {
field public static final int DIRECTION_OUT = 1; // 0x1
}
- public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
- method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- method public void close();
- method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- }
-
public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
}
@@ -31855,6 +31864,10 @@ package android.os.strictmode {
public final class ImplicitDirectBootViolation extends android.os.strictmode.Violation {
}
+ public final class IncorrectContextUseViolation extends android.os.strictmode.Violation {
+ ctor public IncorrectContextUseViolation(@NonNull String, @NonNull Throwable);
+ }
+
public class InstanceCountViolation extends android.os.strictmode.Violation {
method public long getNumberOfInstances();
}
@@ -46368,6 +46381,7 @@ package android.view {
public final class InputDevice implements android.os.Parcelable {
method public int describeContents();
+ method @NonNull public android.hardware.Battery getBattery();
method public int getControllerNumber();
method public String getDescriptor();
method public static android.view.InputDevice getDevice(int);
@@ -46422,6 +46436,7 @@ package android.view {
field public static final int SOURCE_MOUSE = 8194; // 0x2002
field public static final int SOURCE_MOUSE_RELATIVE = 131076; // 0x20004
field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+ field public static final int SOURCE_SENSOR = 67108864; // 0x4000000
field public static final int SOURCE_STYLUS = 16386; // 0x4002
field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
@@ -53063,6 +53078,10 @@ package android.widget {
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet);
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int);
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int, int);
+ method @Deprecated public void setDial(@NonNull android.graphics.drawable.Icon);
+ method @Deprecated public void setHourHand(@NonNull android.graphics.drawable.Icon);
+ method @Deprecated public void setMinuteHand(@NonNull android.graphics.drawable.Icon);
+ method @Deprecated public void setSecondHand(@Nullable android.graphics.drawable.Icon);
}
public class ArrayAdapter<T> extends android.widget.BaseAdapter implements android.widget.Filterable android.widget.ThemedSpinnerAdapter {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 0f49dc580061..e8650faad1f6 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -157,6 +157,10 @@ package android.media.session {
package android.net {
+ public final class ConnectivityFrameworkInitializer {
+ method public static void registerServiceWrappers();
+ }
+
public class ConnectivityManager {
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 3b7e03cf7625..b748b918d1c6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -138,6 +138,7 @@ package android {
field public static final String MANAGE_ROTATION_RESOLVER = "android.permission.MANAGE_ROTATION_RESOLVER";
field public static final String MANAGE_SEARCH_UI = "android.permission.MANAGE_SEARCH_UI";
field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
+ field public static final String MANAGE_SMARTSPACE = "android.permission.MANAGE_SMARTSPACE";
field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
field public static final String MANAGE_TEST_NETWORKS = "android.permission.MANAGE_TEST_NETWORKS";
@@ -1493,6 +1494,169 @@ package android.app.search {
}
+package android.app.smartspace {
+
+ public final class SmartspaceAction implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public CharSequence getContentDescription();
+ method @Nullable public android.os.Bundle getExtras();
+ method @Nullable public android.graphics.drawable.Icon getIcon();
+ method @NonNull public String getId();
+ method @Nullable public android.content.Intent getIntent();
+ method @Nullable public android.app.PendingIntent getPendingIntent();
+ method @Nullable public CharSequence getSubtitle();
+ method @NonNull public CharSequence getTitle();
+ method @Nullable public android.os.UserHandle getUserHandle();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceAction> CREATOR;
+ }
+
+ public static final class SmartspaceAction.Builder {
+ ctor public SmartspaceAction.Builder(@NonNull String, @NonNull String);
+ method @NonNull public android.app.smartspace.SmartspaceAction build();
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setContentDescription(@Nullable CharSequence);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setIcon(@Nullable android.graphics.drawable.Icon);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setIntent(@Nullable android.content.Intent);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setPendingIntent(@Nullable android.app.PendingIntent);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setSubtitle(@Nullable CharSequence);
+ method @NonNull public android.app.smartspace.SmartspaceAction.Builder setUserHandle(@Nullable android.os.UserHandle);
+ }
+
+ public final class SmartspaceConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.os.Bundle getExtras();
+ method @NonNull public String getPackageName();
+ method @NonNull public int getSmartspaceTargetCount();
+ method @NonNull public String getUiSurface();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceConfig> CREATOR;
+ }
+
+ public static final class SmartspaceConfig.Builder {
+ ctor public SmartspaceConfig.Builder(@NonNull android.content.Context, @NonNull String);
+ method @NonNull public android.app.smartspace.SmartspaceConfig build();
+ method @NonNull public android.app.smartspace.SmartspaceConfig.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public android.app.smartspace.SmartspaceConfig.Builder setSmartspaceTargetCount(int);
+ }
+
+ public final class SmartspaceManager {
+ method @NonNull public android.app.smartspace.SmartspaceSession createSmartspaceSession(@NonNull android.app.smartspace.SmartspaceConfig);
+ }
+
+ public final class SmartspaceSession implements java.lang.AutoCloseable {
+ method public void close();
+ method public void destroy();
+ method protected void finalize();
+ method public void notifySmartspaceEvent(@NonNull android.app.smartspace.SmartspaceTargetEvent);
+ method public void registerSmartspaceUpdates(@NonNull java.util.concurrent.Executor, @NonNull android.app.smartspace.SmartspaceSession.Callback);
+ method public void requestSmartspaceUpdate();
+ method public void unregisterSmartspaceUpdates(@NonNull android.app.smartspace.SmartspaceSession.Callback);
+ }
+
+ public static interface SmartspaceSession.Callback {
+ method public void onTargetsAvailable(@NonNull java.util.List<android.app.smartspace.SmartspaceTarget>);
+ }
+
+ public final class SmartspaceSessionId implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getId();
+ method @NonNull public int getUserId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceSessionId> CREATOR;
+ }
+
+ public final class SmartspaceTarget implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.app.smartspace.SmartspaceAction> getActionChips();
+ method @Nullable public String getAssociatedSmartspaceTargetId();
+ method @Nullable public android.app.smartspace.SmartspaceAction getBaseAction();
+ method @NonNull public android.content.ComponentName getComponentName();
+ method @NonNull public long getCreationTimeMillis();
+ method @NonNull public long getExpiryTimeMillis();
+ method @NonNull public int getFeatureType();
+ method @Nullable public android.app.smartspace.SmartspaceAction getHeaderAction();
+ method @NonNull public java.util.List<android.app.smartspace.SmartspaceAction> getIconGrid();
+ method @NonNull public float getScore();
+ method @Nullable public android.net.Uri getSliceUri();
+ method @NonNull public String getSmartspaceTargetId();
+ method @Nullable public String getSourceNotificationKey();
+ method @NonNull public android.os.UserHandle getUserHandle();
+ method @Nullable public android.appwidget.AppWidgetProviderInfo getWidgetId();
+ method @NonNull public boolean isSensitive();
+ method @NonNull public boolean shouldShowExpanded();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceTarget> CREATOR;
+ field public static final int FEATURE_ALARM = 7; // 0x7
+ field public static final int FEATURE_BEDTIME_ROUTINE = 16; // 0x10
+ field public static final int FEATURE_CALENDAR = 2; // 0x2
+ field public static final int FEATURE_COMMUTE_TIME = 3; // 0x3
+ field public static final int FEATURE_CONSENT = 11; // 0xb
+ field public static final int FEATURE_ETA_MONITORING = 18; // 0x12
+ field public static final int FEATURE_FITNESS_TRACKING = 17; // 0x11
+ field public static final int FEATURE_FLIGHT = 4; // 0x4
+ field public static final int FEATURE_LOYALTY_CARD = 14; // 0xe
+ field public static final int FEATURE_MEDIA = 15; // 0xf
+ field public static final int FEATURE_MISSED_CALL = 19; // 0x13
+ field public static final int FEATURE_ONBOARDING = 8; // 0x8
+ field public static final int FEATURE_PACKAGE_TRACKING = 20; // 0x14
+ field public static final int FEATURE_REMINDER = 6; // 0x6
+ field public static final int FEATURE_SHOPPING_LIST = 13; // 0xd
+ field public static final int FEATURE_SPORTS = 9; // 0x9
+ field public static final int FEATURE_STOCK_PRICE_CHANGE = 12; // 0xc
+ field public static final int FEATURE_STOPWATCH = 22; // 0x16
+ field public static final int FEATURE_TIMER = 21; // 0x15
+ field public static final int FEATURE_TIPS = 5; // 0x5
+ field public static final int FEATURE_UNDEFINED = 0; // 0x0
+ field public static final int FEATURE_UPCOMING_ALARM = 23; // 0x17
+ field public static final int FEATURE_WEATHER = 1; // 0x1
+ field public static final int FEATURE_WEATHER_ALERT = 10; // 0xa
+ }
+
+ public static final class SmartspaceTarget.Builder {
+ ctor public SmartspaceTarget.Builder(@NonNull String, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
+ method @NonNull public android.app.smartspace.SmartspaceTarget build();
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setActionChips(@NonNull java.util.List<android.app.smartspace.SmartspaceAction>);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setAssociatedSmartspaceTargetId(@NonNull String);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setBaseAction(@NonNull android.app.smartspace.SmartspaceAction);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setCreationTimeMillis(@NonNull long);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setExpiryTimeMillis(@NonNull long);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setFeatureType(@NonNull int);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setHeaderAction(@NonNull android.app.smartspace.SmartspaceAction);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setIconGrid(@NonNull java.util.List<android.app.smartspace.SmartspaceAction>);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setScore(@NonNull float);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSensitive(@NonNull boolean);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setShouldShowExpanded(@NonNull boolean);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSliceUri(@NonNull android.net.Uri);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSourceNotificationKey(@NonNull String);
+ method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setWidgetId(@NonNull android.appwidget.AppWidgetProviderInfo);
+ }
+
+ public final class SmartspaceTargetEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public int getEventType();
+ method @Nullable public String getSmartspaceActionId();
+ method @Nullable public android.app.smartspace.SmartspaceTarget getSmartspaceTarget();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceTargetEvent> CREATOR;
+ field public static final int EVENT_TARGET_BLOCK = 5; // 0x5
+ field public static final int EVENT_TARGET_DISMISS = 4; // 0x4
+ field public static final int EVENT_TARGET_INTERACTION = 1; // 0x1
+ field public static final int EVENT_TARGET_IN_VIEW = 2; // 0x2
+ field public static final int EVENT_TARGET_OUT_OF_VIEW = 3; // 0x3
+ field public static final int EVENT_UI_SURFACE_IN_VIEW = 6; // 0x6
+ field public static final int EVENT_UI_SURFACE_OUT_OF_VIEW = 7; // 0x7
+ }
+
+ public static final class SmartspaceTargetEvent.Builder {
+ ctor public SmartspaceTargetEvent.Builder(int);
+ method @NonNull public android.app.smartspace.SmartspaceTargetEvent build();
+ method @NonNull public android.app.smartspace.SmartspaceTargetEvent.Builder setSmartspaceActionId(@NonNull String);
+ method @NonNull public android.app.smartspace.SmartspaceTargetEvent.Builder setSmartspaceTarget(@NonNull android.app.smartspace.SmartspaceTarget);
+ }
+
+}
+
package android.app.time {
public final class TimeManager {
@@ -1955,6 +2119,7 @@ package android.content {
field public static final String ROLLBACK_SERVICE = "rollback";
field public static final String SEARCH_UI_SERVICE = "search_ui";
field public static final String SECURE_ELEMENT_SERVICE = "secure_element";
+ field public static final String SMARTSPACE_SERVICE = "smartspace";
field public static final String STATS_MANAGER = "stats";
field public static final String STATUS_BAR_SERVICE = "statusbar";
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
@@ -3183,6 +3348,7 @@ package android.hardware.location {
public class ContextHubClientCallback {
ctor public ContextHubClientCallback();
+ method public void onClientAuthorizationChanged(@NonNull android.hardware.location.ContextHubClient, long, int);
method public void onHubReset(android.hardware.location.ContextHubClient);
method public void onMessageFromNanoApp(android.hardware.location.ContextHubClient, android.hardware.location.NanoAppMessage);
method public void onNanoAppAborted(android.hardware.location.ContextHubClient, long, int);
@@ -3219,6 +3385,7 @@ package android.hardware.location {
public class ContextHubIntentEvent {
method @NonNull public static android.hardware.location.ContextHubIntentEvent fromIntent(@NonNull android.content.Intent);
+ method public int getClientAuthorizationState();
method @NonNull public android.hardware.location.ContextHubInfo getContextHubInfo();
method public int getEventType();
method public int getNanoAppAbortCode();
@@ -3227,8 +3394,10 @@ package android.hardware.location {
}
public final class ContextHubManager {
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.location.ContextHubClientCallback);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback, @NonNull java.util.concurrent.Executor);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
@@ -3246,6 +3415,10 @@ package android.hardware.location {
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int unloadNanoApp(int);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
method @Deprecated public int unregisterCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
+ field public static final int AUTHORIZATION_DENIED = 0; // 0x0
+ field public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1; // 0x1
+ field public static final int AUTHORIZATION_GRANTED = 2; // 0x2
+ field public static final int EVENT_CLIENT_AUTHORIZATION = 7; // 0x7
field public static final int EVENT_HUB_RESET = 6; // 0x6
field public static final int EVENT_NANOAPP_ABORTED = 4; // 0x4
field public static final int EVENT_NANOAPP_DISABLED = 3; // 0x3
@@ -3253,6 +3426,7 @@ package android.hardware.location {
field public static final int EVENT_NANOAPP_LOADED = 0; // 0x0
field public static final int EVENT_NANOAPP_MESSAGE = 5; // 0x5
field public static final int EVENT_NANOAPP_UNLOADED = 1; // 0x1
+ field public static final String EXTRA_CLIENT_AUTHORIZATION_STATE = "android.hardware.location.extra.CLIENT_AUTHORIZATION_STATE";
field public static final String EXTRA_CONTEXT_HUB_INFO = "android.hardware.location.extra.CONTEXT_HUB_INFO";
field public static final String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
field public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
@@ -3491,8 +3665,10 @@ package android.hardware.location {
public final class NanoAppState implements android.os.Parcelable {
ctor public NanoAppState(long, int, boolean);
+ ctor public NanoAppState(long, int, boolean, @NonNull java.util.List<java.lang.String>);
method public int describeContents();
method public long getNanoAppId();
+ method @NonNull public java.util.List<java.lang.String> getNanoAppPermissions();
method public long getNanoAppVersion();
method public boolean isEnabled();
method public void writeToParcel(android.os.Parcel, int);
@@ -5817,6 +5993,7 @@ package android.media.tv.tuner.filter {
public final class RestartEvent extends android.media.tv.tuner.filter.FilterEvent {
method public int getStartId();
+ field public static final int NEW_FILTER_FIRST_START_ID = 0; // 0x0
}
public final class ScramblingStatusEvent extends android.media.tv.tuner.filter.FilterEvent {
@@ -6951,11 +7128,15 @@ package android.net {
}
public final class IpSecManager {
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
}
public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+ method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
+ method public void close();
method @NonNull public String getInterfaceName();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
}
public static class IpSecTransform.Builder {
@@ -7100,6 +7281,7 @@ package android.net {
method @Nullable public String getSsid();
method @NonNull public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+ field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
@@ -9846,6 +10028,7 @@ package android.service.notification {
method public void onNotificationDirectReplied(@NonNull String);
method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification);
method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
+ method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel, @NonNull android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String);
method public void onNotificationVisibilityChanged(@NonNull String, boolean);
@@ -10071,6 +10254,21 @@ package android.service.settings.suggestions {
}
+package android.service.smartspace {
+
+ public abstract class SmartspaceService extends android.app.Service {
+ ctor public SmartspaceService();
+ method @MainThread public abstract void notifySmartspaceEvent(@NonNull android.app.smartspace.SmartspaceSessionId, @NonNull android.app.smartspace.SmartspaceTargetEvent);
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onCreateSmartspaceSession(@NonNull android.app.smartspace.SmartspaceConfig, @NonNull android.app.smartspace.SmartspaceSessionId);
+ method @MainThread public abstract void onDestroy(@NonNull android.app.smartspace.SmartspaceSessionId);
+ method public abstract void onDestroySmartspaceSession(@NonNull android.app.smartspace.SmartspaceSessionId);
+ method @MainThread public abstract void onRequestSmartspaceUpdate(@NonNull android.app.smartspace.SmartspaceSessionId);
+ method public final void updateSmartspaceTargets(@NonNull android.app.smartspace.SmartspaceSessionId, @NonNull java.util.List<android.app.smartspace.SmartspaceTarget>);
+ }
+
+}
+
package android.service.storage {
public abstract class ExternalStorageService extends android.app.Service {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 8b3cee403bca..58f6c52d2b05 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -6,9 +6,11 @@ ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions():
BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex):
Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.IpSecTransform.Builder.buildTunnelModeTransform(java.net.InetAddress,android.net.IpSecManager.SecurityParameterIndex)
+
ExecutorRegistration: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler):
Registration methods should have overload that accepts delivery Executor: `setOnRtpRxNoticeListener`
-
+
+
GenericException: android.app.prediction.AppPredictor#finalize():
GenericException: android.hardware.location.ContextHubClient#finalize():
@@ -21,6 +23,8 @@ GenericException: android.service.autofill.augmented.FillWindow#finalize():
IntentBuilderName: android.app.search.SearchAction#getIntent():
+IntentBuilderName: android.app.smartspace.SmartspaceAction#getIntent():
+ Methods creating an Intent should be named `create<Foo>Intent()`, was `getIntent`
KotlinKeyword: android.app.Notification#when:
@@ -83,6 +87,10 @@ MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(
+OnNameExpected: android.service.smartspace.SmartspaceService#notifySmartspaceEvent(android.app.smartspace.SmartspaceSessionId, android.app.smartspace.SmartspaceTargetEvent):
+ Methods implemented by developers should follow the on<Something> style, was `notifySmartspaceEvent`
+
+
ProtectedMember: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
ProtectedMember: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]):
@@ -187,11 +195,10 @@ SamShouldBeLast: android.media.AudioRecordingMonitor#registerAudioRecordingCallb
SamShouldBeLast: android.media.AudioRouting#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
-SamShouldBeLast: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler):
- SAM-compatible parameters (such as parameter 2, "listener", in android.media.MediaPlayer.setOnRtpRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-
SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+SamShouldBeLast: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 2, "listener", in android.media.MediaPlayer.setOnRtpRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.media.MediaRecorder#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
SamShouldBeLast: android.media.MediaRecorder#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback):
@@ -258,3 +265,5 @@ UserHandleName: android.app.search.SearchAction.Builder#setUserHandle(android.os
Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `setUserHandle`
UserHandleName: android.app.search.SearchTarget.Builder#setUserHandle(android.os.UserHandle):
+UserHandleName: android.app.smartspace.SmartspaceAction.Builder#setUserHandle(android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `setUserHandle`
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bc1858b63783..b0ee3f01f210 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2324,6 +2324,8 @@ package android.view {
}
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
+ method @Nullable public final android.os.IBinder getWindowContextToken();
+ method public final void setWindowContextToken(@NonNull android.os.IBinder);
field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
field public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 64; // 0x40
field public CharSequence accessibilityTitle;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9b141b7bc649..1f9cb6430a5d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2291,9 +2291,10 @@ public final class ActivityThread extends ClientTransactionHandler {
* Resources if one has already been created.
*/
Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
- String[] libDirs, LoadedApk pkgInfo) {
+ String[] libDirs, LoadedApk pkgInfo, Configuration overrideConfig) {
return mResourcesManager.getResources(null, resDir, splitResDirs, overlayDirs, libDirs,
- null, null, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(), null);
+ null, overrideConfig, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(),
+ null);
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index d716a3cf5445..7410a1ca04c8 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -50,7 +50,7 @@ import java.util.zip.GZIPInputStream;
*
* <p>
* Application process could die for many reasons, for example {@link #REASON_LOW_MEMORY}
- * when it was killed by the ystem because it was running low on memory. Reason
+ * when it was killed by the system because it was running low on memory. Reason
* of the death can be retrieved via {@link #getReason}. Besides the reason, there are a few other
* auxiliary APIs like {@link #getStatus} and {@link #getImportance} to help the caller with
* additional diagnostic information.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 77542bda22a0..7e7f887766e2 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -70,6 +70,7 @@ import android.content.pm.SuspendDialogInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
@@ -1691,20 +1692,29 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public Resources getResourcesForApplication(@NonNull ApplicationInfo app)
throws NameNotFoundException {
+ return getResourcesForApplication(app, null);
+ }
+
+ @Override
+ public Resources getResourcesForApplication(@NonNull ApplicationInfo app,
+ @Nullable Configuration configuration) throws NameNotFoundException {
if (app.packageName.equals("system")) {
- return mContext.mMainThread.getSystemUiContext().getResources();
+ Context sysuiContext = mContext.mMainThread.getSystemUiContext();
+ if (configuration != null) {
+ sysuiContext = sysuiContext.createConfigurationContext(configuration);
+ }
+ return sysuiContext.getResources();
}
final boolean sameUid = (app.uid == Process.myUid());
final Resources r = mContext.mMainThread.getTopLevelResources(
- sameUid ? app.sourceDir : app.publicSourceDir,
- sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
- app.resourceDirs, app.sharedLibraryFiles,
- mContext.mPackageInfo);
+ sameUid ? app.sourceDir : app.publicSourceDir,
+ sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
+ app.resourceDirs, app.sharedLibraryFiles,
+ mContext.mPackageInfo, configuration);
if (r != null) {
return r;
}
throw new NameNotFoundException("Unable to open " + app.publicSourceDir);
-
}
@Override
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 534f3e25acde..671315f37b20 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -21,6 +21,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemApi.Client;
import android.annotation.TestApi;
@@ -1275,6 +1276,7 @@ public final class PendingIntent implements Parcelable {
* @param flags MATCH_* flags from {@link android.content.pm.PackageManager}.
* @hide
*/
+ @SuppressLint("NullableCollection")
@RequiresPermission(permission.GET_INTENT_SENDER_INTENT)
@SystemApi(client = Client.MODULE_LIBRARIES)
@TestApi
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 1498dae764a9..f05c7b58e202 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -34,6 +34,7 @@ import android.app.prediction.AppPredictionManager;
import android.app.role.RoleFrameworkInitializer;
import android.app.search.SearchUiManager;
import android.app.slice.SliceManager;
+import android.app.smartspace.SmartspaceManager;
import android.app.time.TimeManager;
import android.app.timedetector.TimeDetector;
import android.app.timedetector.TimeDetectorImpl;
@@ -120,21 +121,16 @@ import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
import android.media.tv.tunerresourcemanager.ITunerResourceManager;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
-import android.net.ConnectivityDiagnosticsManager;
-import android.net.ConnectivityManager;
+import android.net.ConnectivityFrameworkInitializer;
import android.net.EthernetManager;
-import android.net.IConnectivityManager;
import android.net.IEthernetManager;
import android.net.IIpSecService;
import android.net.INetworkPolicyManager;
-import android.net.ITestNetworkManager;
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
import android.net.NetworkWatchlistManager;
-import android.net.TestNetworkManager;
import android.net.TetheringManager;
-import android.net.VpnManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
@@ -163,7 +159,6 @@ import android.os.IUserManager;
import android.os.IncidentManager;
import android.os.PowerManager;
import android.os.RecoverySystem;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StatsFrameworkInitializer;
@@ -370,15 +365,6 @@ public final class SystemServiceRegistry {
// (which extends it).
SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);
- registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
- new StaticApplicationContextServiceFetcher<ConnectivityManager>() {
- @Override
- public ConnectivityManager createService(Context context) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- return new ConnectivityManager(context, service);
- }});
-
registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
@Override
public IBinder createService() throws ServiceNotFoundException {
@@ -412,50 +398,6 @@ public final class SystemServiceRegistry {
return new IpSecManager(ctx, service);
}});
- registerService(Context.VPN_MANAGEMENT_SERVICE, VpnManager.class,
- new CachedServiceFetcher<VpnManager>() {
- @Override
- public VpnManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- return new VpnManager(ctx, service);
- }});
-
- registerService(Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
- ConnectivityDiagnosticsManager.class,
- new CachedServiceFetcher<ConnectivityDiagnosticsManager>() {
- @Override
- public ConnectivityDiagnosticsManager createService(ContextImpl ctx)
- throws ServiceNotFoundException {
- // ConnectivityDiagnosticsManager is backed by ConnectivityService
- IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- return new ConnectivityDiagnosticsManager(ctx, service);
- }});
-
- registerService(
- Context.TEST_NETWORK_SERVICE,
- TestNetworkManager.class,
- new StaticApplicationContextServiceFetcher<TestNetworkManager>() {
- @Override
- public TestNetworkManager createService(Context context)
- throws ServiceNotFoundException {
- IBinder csBinder =
- ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager csMgr =
- IConnectivityManager.Stub.asInterface(csBinder);
-
- final IBinder tnBinder;
- try {
- tnBinder = csMgr.startOrGetTestNetworkService();
- } catch (RemoteException e) {
- throw new ServiceNotFoundException(Context.TEST_NETWORK_SERVICE);
- }
- ITestNetworkManager tnMgr = ITestNetworkManager.Stub.asInterface(tnBinder);
- return new TestNetworkManager(tnMgr);
- }
- });
-
registerService(Context.COUNTRY_DETECTOR, CountryDetector.class,
new StaticServiceFetcher<CountryDetector>() {
@Override
@@ -1224,6 +1166,16 @@ public final class SystemServiceRegistry {
}
});
+ registerService(Context.SMARTSPACE_SERVICE, SmartspaceManager.class,
+ new CachedServiceFetcher<SmartspaceManager>() {
+ @Override
+ public SmartspaceManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getService(Context.SMARTSPACE_SERVICE);
+ return b == null ? null : new SmartspaceManager(ctx);
+ }
+ });
+
registerService(Context.APP_PREDICTION_SERVICE, AppPredictionManager.class,
new CachedServiceFetcher<AppPredictionManager>() {
@Override
@@ -1441,6 +1393,7 @@ public final class SystemServiceRegistry {
try {
// Note: the following functions need to be @SystemApis, once they become mainline
// modules.
+ ConnectivityFrameworkInitializer.registerServiceWrappers();
JobSchedulerFrameworkInitializer.registerServiceWrappers();
BlobStoreManagerFrameworkInitializer.initialize();
TelephonyFrameworkInitializer.registerServiceWrappers();
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 623c878d7bfa..e31e0243cae0 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -36,7 +36,6 @@ import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import java.util.ArrayList;
-import java.util.List;
import java.util.Objects;
/**
@@ -180,6 +179,19 @@ public class TaskInfo {
public ActivityInfo topActivityInfo;
/**
+ * The top activity in this task.
+ * @hide
+ */
+ @Nullable
+ public IBinder topActivityToken;
+
+ /**
+ * Whether the direct top activity is in size compat mode on foreground.
+ * @hide
+ */
+ public boolean topActivityInSizeCompat;
+
+ /**
* Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity
* supports), this is what the system actually uses for resizability based on other policy and
* developer options.
@@ -356,6 +368,8 @@ public class TaskInfo {
parentTaskId = source.readInt();
isFocused = source.readBoolean();
isVisible = source.readBoolean();
+ topActivityToken = source.readStrongBinder();
+ topActivityInSizeCompat = source.readBoolean();
}
/**
@@ -391,6 +405,8 @@ public class TaskInfo {
dest.writeInt(parentTaskId);
dest.writeBoolean(isFocused);
dest.writeBoolean(isVisible);
+ dest.writeStrongBinder(topActivityToken);
+ dest.writeBoolean(topActivityInSizeCompat);
}
@Override
@@ -415,6 +431,8 @@ public class TaskInfo {
+ " parentTaskId=" + parentTaskId
+ " isFocused=" + isFocused
+ " isVisible=" + isVisible
+ + " topActivityToken=" + topActivityToken
+ + " topActivityInSizeCompat=" + topActivityInSizeCompat
+ "}";
}
}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 65a21640bb98..2b52875f4b0b 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -2,6 +2,7 @@ package android.app.assist;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Activity;
import android.content.ComponentName;
@@ -1542,6 +1543,7 @@ public class AssistStructure implements Parcelable {
* {@link View#getOnReceiveContentMimeTypes()} for details.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public String[] getOnReceiveContentMimeTypes() {
return mOnReceiveContentMimeTypes;
}
diff --git a/core/java/android/app/search/Query.java b/core/java/android/app/search/Query.java
index 447ca314b99b..3ab20bb30b22 100644
--- a/core/java/android/app/search/Query.java
+++ b/core/java/android/app/search/Query.java
@@ -17,6 +17,7 @@ package android.app.search;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
@@ -46,6 +47,7 @@ public final class Query implements Parcelable {
public Query(@NonNull String input,
long timestamp,
+ @SuppressLint("NullableCollection")
@Nullable Bundle extras) {
mInput = input;
mTimestamp = timestamp;
@@ -69,6 +71,7 @@ public final class Query implements Parcelable {
}
@Nullable
+ @SuppressLint("NullableCollection")
public Bundle getExtras() {
return mExtras;
}
diff --git a/core/java/android/app/search/SearchAction.java b/core/java/android/app/search/SearchAction.java
index a76154af63b6..9e40e7ebaef0 100644
--- a/core/java/android/app/search/SearchAction.java
+++ b/core/java/android/app/search/SearchAction.java
@@ -18,6 +18,7 @@ package android.app.search;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.content.Intent;
@@ -167,6 +168,7 @@ public final class SearchAction implements Parcelable {
/**
* Returns the extra bundle for this object.
*/
+ @SuppressLint("NullableCollection")
public @Nullable Bundle getExtras() {
return mExtras;
}
@@ -325,7 +327,8 @@ public final class SearchAction implements Parcelable {
* Sets the extra.
*/
@NonNull
- public SearchAction.Builder setExtras(@Nullable Bundle extras) {
+ public SearchAction.Builder setExtras(
+ @SuppressLint("NullableCollection") @Nullable Bundle extras) {
mExtras = extras;
return this;
}
diff --git a/core/java/android/app/search/SearchContext.java b/core/java/android/app/search/SearchContext.java
index 9bf766dc8668..548b7daff3a6 100644
--- a/core/java/android/app/search/SearchContext.java
+++ b/core/java/android/app/search/SearchContext.java
@@ -17,6 +17,7 @@ package android.app.search;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
@@ -52,7 +53,7 @@ public final class SearchContext implements Parcelable {
public SearchContext(int resultTypes,
int queryTimeoutMillis,
- @Nullable Bundle extras) {
+ @SuppressLint("NullableCollection") @Nullable Bundle extras) {
mResultTypes = resultTypes;
mTimeoutMillis = queryTimeoutMillis;
mExtras = extras;
@@ -83,6 +84,7 @@ public final class SearchContext implements Parcelable {
}
@Nullable
+ @SuppressLint("NullableCollection")
public Bundle getExtras() {
return mExtras;
}
diff --git a/core/java/android/app/search/SearchTarget.java b/core/java/android/app/search/SearchTarget.java
index cac22d81c67b..6a80f8bd222a 100644
--- a/core/java/android/app/search/SearchTarget.java
+++ b/core/java/android/app/search/SearchTarget.java
@@ -17,6 +17,7 @@ package android.app.search;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.appwidget.AppWidgetProviderInfo;
import android.content.pm.ShortcutInfo;
@@ -224,6 +225,7 @@ public final class SearchTarget implements Parcelable {
* Return extra bundle.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public Bundle getExtras() {
return mExtras;
}
@@ -386,7 +388,7 @@ public final class SearchTarget implements Parcelable {
* TODO: add comment
*/
@NonNull
- public Builder setExtras(@Nullable Bundle extras) {
+ public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) {
mExtras = extras;
return this;
}
diff --git a/core/java/android/app/smartspace/ISmartspaceCallback.aidl b/core/java/android/app/smartspace/ISmartspaceCallback.aidl
new file mode 100644
index 000000000000..df105f9db7c1
--- /dev/null
+++ b/core/java/android/app/smartspace/ISmartspaceCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace;
+
+import android.content.pm.ParceledListSlice;
+
+/**
+ * @hide
+ */
+oneway interface ISmartspaceCallback {
+
+ void onResult(in ParceledListSlice result);
+}
diff --git a/core/java/android/app/smartspace/ISmartspaceManager.aidl b/core/java/android/app/smartspace/ISmartspaceManager.aidl
new file mode 100644
index 000000000000..e7ec8891ea83
--- /dev/null
+++ b/core/java/android/app/smartspace/ISmartspaceManager.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace;
+
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.ISmartspaceCallback;
+import android.content.pm.ParceledListSlice;
+
+/**
+ * @hide
+ */
+interface ISmartspaceManager {
+
+ void createSmartspaceSession(in SmartspaceConfig config, in SmartspaceSessionId sessionId,
+ in IBinder token);
+
+ void notifySmartspaceEvent(in SmartspaceSessionId sessionId, in SmartspaceTargetEvent event);
+
+ void requestSmartspaceUpdate(in SmartspaceSessionId sessionId);
+
+ void registerSmartspaceUpdates(in SmartspaceSessionId sessionId,
+ in ISmartspaceCallback callback);
+
+ void unregisterSmartspaceUpdates(in SmartspaceSessionId sessionId,
+ in ISmartspaceCallback callback);
+
+ void destroySmartspaceSession(in SmartspaceSessionId sessionId);
+}
diff --git a/core/java/android/app/smartspace/SmartspaceAction.java b/core/java/android/app/smartspace/SmartspaceAction.java
new file mode 100644
index 000000000000..033cda1ba845
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceAction.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * A {@link SmartspaceAction} represents an action which can be taken by a user by tapping on either
+ * the title, the subtitle or on the icon. Supported instances are Intents, PendingIntents or a
+ * ShortcutInfo (by putting the ShortcutInfoId in the bundle). These actions can be called from
+ * another process or within the client process.
+ *
+ * Clients can also receive conditional Intents/PendingIntents in the extras bundle which are
+ * supposed to be fired when the conditions are met. For example, a user can invoke a dismiss/block
+ * action on a game score card but the intention is to only block the team and not the entire
+ * feature.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceAction implements Parcelable {
+
+ private static final String TAG = "SmartspaceAction";
+
+ /** A unique Id of this {@link SmartspaceAction}. */
+ @NonNull
+ private final String mId;
+
+ /** An Icon which can be displayed in the UI. */
+ @Nullable
+ private final Icon mIcon;
+
+ /** Title associated with an action. */
+ @NonNull
+ private final CharSequence mTitle;
+
+ /** Subtitle associated with an action. */
+ @Nullable
+ private final CharSequence mSubtitle;
+
+ @Nullable
+ private final CharSequence mContentDescription;
+
+ @Nullable
+ private final PendingIntent mPendingIntent;
+
+ @Nullable
+ private final Intent mIntent;
+
+ @Nullable
+ private final UserHandle mUserHandle;
+
+ @Nullable
+ private Bundle mExtras;
+
+ SmartspaceAction(Parcel in) {
+ mId = in.readString();
+ mIcon = in.readTypedObject(Icon.CREATOR);
+ mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+ mIntent = in.readTypedObject(Intent.CREATOR);
+ mUserHandle = in.readTypedObject(UserHandle.CREATOR);
+ mExtras = in.readTypedObject(Bundle.CREATOR);
+ }
+
+ private SmartspaceAction(
+ @NonNull String id,
+ @Nullable Icon icon,
+ @NonNull CharSequence title,
+ @Nullable CharSequence subtitle,
+ @Nullable CharSequence contentDescription,
+ @Nullable PendingIntent pendingIntent,
+ @Nullable Intent intent,
+ @Nullable UserHandle userHandle,
+ @Nullable Bundle extras) {
+ mId = Objects.requireNonNull(id);
+ mIcon = icon;
+ mTitle = Objects.requireNonNull(title);
+ mSubtitle = subtitle;
+ mContentDescription = contentDescription;
+ mPendingIntent = pendingIntent;
+ mIntent = intent;
+ mUserHandle = userHandle;
+ mExtras = extras;
+ }
+
+ /**
+ * Returns the unique id of this object.
+ */
+ public @NonNull String getId() {
+ return mId;
+ }
+
+ /**
+ * Returns an icon representing the action.
+ */
+ public @Nullable Icon getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Returns a title representing the action.
+ */
+ public @NonNull CharSequence getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Returns a subtitle representing the action.
+ */
+ public @Nullable CharSequence getSubtitle() {
+ return mSubtitle;
+ }
+
+ /**
+ * Returns a content description representing the action.
+ */
+ public @Nullable CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
+ * Returns the action intent.
+ */
+ public @Nullable PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+
+ /**
+ * Returns the intent.
+ */
+ public @Nullable Intent getIntent() {
+ return mIntent;
+ }
+
+ /**
+ * Returns the user handle.
+ */
+ public @Nullable UserHandle getUserHandle() {
+ return mUserHandle;
+ }
+
+ /**
+ * Returns the extra bundle for this object.
+ */
+ public @Nullable Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceAction)) return false;
+ SmartspaceAction that = (SmartspaceAction) o;
+ return mId.equals(that.mId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeString(mId);
+ out.writeTypedObject(mIcon, flags);
+ TextUtils.writeToParcel(mTitle, out, flags);
+ TextUtils.writeToParcel(mSubtitle, out, flags);
+ TextUtils.writeToParcel(mContentDescription, out, flags);
+ out.writeTypedObject(mPendingIntent, flags);
+ out.writeTypedObject(mIntent, flags);
+ out.writeTypedObject(mUserHandle, flags);
+ out.writeBundle(mExtras);
+ }
+
+ @Override
+ public String toString() {
+ return "SmartspaceAction{"
+ + "mId='" + mId + '\''
+ + ", mIcon=" + mIcon
+ + ", mTitle=" + mTitle
+ + ", mSubtitle=" + mSubtitle
+ + ", mContentDescription=" + mContentDescription
+ + ", mPendingIntent=" + mPendingIntent
+ + ", mIntent=" + mIntent
+ + ", mUserHandle=" + mUserHandle
+ + ", mExtras=" + mExtras
+ + '}';
+ }
+
+ public static final @NonNull Creator<SmartspaceAction> CREATOR =
+ new Creator<SmartspaceAction>() {
+ public SmartspaceAction createFromParcel(Parcel in) {
+ return new SmartspaceAction(in);
+ }
+ public SmartspaceAction[] newArray(int size) {
+ return new SmartspaceAction[size];
+ }
+ };
+
+ /**
+ * A builder for Smartspace action object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ @NonNull
+ private String mId;
+
+ @Nullable
+ private Icon mIcon;
+
+ @NonNull
+ private CharSequence mTitle;
+
+ @Nullable
+ private CharSequence mSubtitle;
+
+ @Nullable
+ private CharSequence mContentDescription;
+
+ @Nullable
+ private PendingIntent mPendingIntent;
+
+ @Nullable
+ private Intent mIntent;
+
+ @Nullable
+ private UserHandle mUserHandle;
+
+ @Nullable
+ private Bundle mExtras;
+
+ /**
+ * Id and title are required.
+ */
+ public Builder(@NonNull String id, @NonNull String title) {
+ mId = Objects.requireNonNull(id);
+ mTitle = Objects.requireNonNull(title);
+ }
+
+ /**
+ * Sets the icon.
+ */
+ @NonNull
+ public Builder setIcon(
+ @Nullable Icon icon) {
+ mIcon = icon;
+ return this;
+ }
+
+ /**
+ * Sets the subtitle.
+ */
+ @NonNull
+ public Builder setSubtitle(
+ @Nullable CharSequence subtitle) {
+ mSubtitle = subtitle;
+ return this;
+ }
+
+ /**
+ * Sets the content description.
+ */
+ @NonNull
+ public Builder setContentDescription(
+ @Nullable CharSequence contentDescription) {
+ mContentDescription = contentDescription;
+ return this;
+ }
+
+ /**
+ * Sets the pending intent.
+ */
+ @NonNull
+ public Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
+ mPendingIntent = pendingIntent;
+ return this;
+ }
+
+ /**
+ * Sets the user handle.
+ */
+ @NonNull
+ public Builder setUserHandle(@Nullable UserHandle userHandle) {
+ mUserHandle = userHandle;
+ return this;
+ }
+
+ /**
+ * Sets the intent.
+ */
+ @NonNull
+ public Builder setIntent(@Nullable Intent intent) {
+ mIntent = intent;
+ return this;
+ }
+
+ /**
+ * Sets the extra.
+ */
+ @NonNull
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Builds a new SmartspaceAction instance.
+ *
+ * @throws IllegalStateException if no target is set
+ */
+ @NonNull
+ public SmartspaceAction build() {
+ return new SmartspaceAction(mId, mIcon, mTitle, mSubtitle, mContentDescription,
+ mPendingIntent, mIntent, mUserHandle, mExtras);
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceConfig.aidl b/core/java/android/app/smartspace/SmartspaceConfig.aidl
new file mode 100644
index 000000000000..136b6f412b54
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceConfig.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace;
+
+parcelable SmartspaceConfig; \ No newline at end of file
diff --git a/core/java/android/app/smartspace/SmartspaceConfig.java b/core/java/android/app/smartspace/SmartspaceConfig.java
new file mode 100644
index 000000000000..1f9cbb5ca33b
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceConfig.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A {@link SmartspaceConfig} instance is supposed to be created by a smartspace client for each
+ * UISurface. The client can specify some initialization conditions for the UISurface like its name,
+ * expected number of smartspace cards etc. The clients can also specify if they want periodic
+ * updates or their desired maximum refresh frequency.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceConfig implements Parcelable {
+
+ /**
+ * The least number of smartspace targets expected to be predicted by the backend. The backend
+ * will always try to satisfy this threshold but it is not guaranteed to always meet it.
+ */
+ private final int mSmartspaceTargetCount;
+
+ /**
+ * A {@link mUiSurface} is the name of the surface which will be used to display the cards. A
+ * few examples are homescreen, lockscreen, aod etc.
+ */
+ @NonNull
+ private final String mUiSurface;
+
+ /** Package name of the client. */
+ @NonNull
+ private String mPackageName;
+
+ /** Send other client UI configurations in extras.
+ *
+ * This can include:
+ *
+ * - Desired maximum update frequency
+ * - Request to get periodic updates
+ * - Request to support multiple clients for the same UISurface.
+ */
+ @Nullable
+ private final Bundle mExtras;
+
+ private SmartspaceConfig(@NonNull String uiSurface, int numPredictedTargets,
+ @NonNull String packageName, @Nullable Bundle extras) {
+ mUiSurface = uiSurface;
+ mSmartspaceTargetCount = numPredictedTargets;
+ mPackageName = packageName;
+ mExtras = extras;
+ }
+
+ private SmartspaceConfig(Parcel parcel) {
+ mUiSurface = parcel.readString();
+ mSmartspaceTargetCount = parcel.readInt();
+ mPackageName = parcel.readString();
+ mExtras = parcel.readBundle();
+ }
+
+ /** Returns the package name of the prediction context. */
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /** Returns the number of smartspace targets requested by the user. */
+ @NonNull
+ public int getSmartspaceTargetCount() {
+ return mSmartspaceTargetCount;
+ }
+
+ /** Returns the UISurface requested by the client. */
+ @NonNull
+ public String getUiSurface() {
+ return mUiSurface;
+ }
+
+ @Nullable
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mUiSurface);
+ dest.writeInt(mSmartspaceTargetCount);
+ dest.writeString(mPackageName);
+ dest.writeBundle(mExtras);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SmartspaceConfig that = (SmartspaceConfig) o;
+ return mSmartspaceTargetCount == that.mSmartspaceTargetCount
+ && Objects.equals(mUiSurface, that.mUiSurface)
+ && Objects.equals(mPackageName, that.mPackageName)
+ && Objects.equals(mExtras, that.mExtras);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSmartspaceTargetCount, mUiSurface, mPackageName, mExtras);
+ }
+
+ /**
+ * @see Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceConfig> CREATOR =
+ new Creator<SmartspaceConfig>() {
+ public SmartspaceConfig createFromParcel(Parcel parcel) {
+ return new SmartspaceConfig(parcel);
+ }
+
+ public SmartspaceConfig[] newArray(int size) {
+ return new SmartspaceConfig[size];
+ }
+ };
+
+ /**
+ * A builder for {@link SmartspaceConfig}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ @NonNull
+ private int mSmartspaceTargetCount = 5; // Default count is 5
+ @NonNull
+ private final String mUiSurface;
+ @NonNull
+ private final String mPackageName;
+ @NonNull
+ private Bundle mExtras = Bundle.EMPTY;
+
+ /**
+ * @param context The {@link Context} which is used to fetch the package name.
+ * @param uiSurface the UI Surface name associated with this context.
+ * @hide
+ */
+ @SystemApi
+ public Builder(@NonNull Context context, @NonNull String uiSurface) {
+ mPackageName = context.getPackageName();
+ this.mUiSurface = uiSurface;
+ }
+
+ /**
+ * Used to set the expected number of cards for this context.
+ */
+ @NonNull
+ public Builder setSmartspaceTargetCount(int smartspaceTargetCount) {
+ this.mSmartspaceTargetCount = smartspaceTargetCount;
+ return this;
+ }
+
+ /**
+ * Used to send a bundle containing extras for the {@link SmartspaceConfig}.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle extras) {
+ this.mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Returns an instance of {@link SmartspaceConfig}.
+ */
+ @NonNull
+ public SmartspaceConfig build() {
+ return new SmartspaceConfig(mUiSurface, mSmartspaceTargetCount, mPackageName, mExtras);
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceManager.java b/core/java/android/app/smartspace/SmartspaceManager.java
new file mode 100644
index 000000000000..ff5eb109dfe0
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceManager.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+
+import java.util.Objects;
+
+/**
+ * Smartspace is a container in Android which is used to show contextual content powered by the
+ * intelligence service running on the device. A smartspace container can be on AoD, lockscreen or
+ * on the homescreen and can show personalized cards which are either derived from on device or
+ * online signals.
+ *
+ * {@link SmartspaceManager} is a system service that provides methods to create Smartspace session
+ * clients. An instance of this class is returned when a client calls
+ * <code> context.getSystemService("smartspace"); </code>.
+ *
+ * After receiving the service, a client must call
+ * {@link SmartspaceManager#createSmartspaceSession(SmartspaceConfig)} with a corresponding
+ * {@link SmartspaceConfig} to get an instance of {@link SmartspaceSession}.
+ * This session is then a client's point of contact with the api. They can send events, request for
+ * updates using the session. It is client's duty to call {@link SmartspaceSession#destroy()} to
+ * destroy the session once they no longer need it.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceManager {
+
+ private final Context mContext;
+
+ /**
+ * @hide
+ */
+ public SmartspaceManager(Context context) {
+ mContext = Objects.requireNonNull(context);
+ }
+
+ /**
+ * Creates a new Smartspace session.
+ */
+ @NonNull
+ public SmartspaceSession createSmartspaceSession(
+ @NonNull SmartspaceConfig smartspaceConfig) {
+ return new SmartspaceSession(mContext, smartspaceConfig);
+ }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceSession.java b/core/java/android/app/smartspace/SmartspaceSession.java
new file mode 100644
index 000000000000..16def61239cf
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceSession.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.smartspace;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.smartspace.ISmartspaceCallback.Stub;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import dalvik.system.CloseGuard;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+/**
+ * Client API to share information about the Smartspace UI state and execute query.
+ *
+ * <p>
+ * Usage: <pre> {@code
+ *
+ * class MyActivity {
+ * private SmartspaceSession mSmartspaceSession;
+ *
+ * void onCreate() {
+ * mSmartspaceSession = mSmartspaceManager.createSmartspaceSession(smartspaceConfig)
+ * mSmartspaceSession.registerSmartspaceUpdates(...)
+ * }
+ *
+ * void onStart() {
+ * mSmartspaceSession.requestSmartspaceUpdate()
+ * }
+ *
+ * void onTouch(...) OR
+ * void onStateTransitionStarted(...) OR
+ * void onResume(...) OR
+ * void onStop(...) {
+ * mSmartspaceSession.notifyEvent(event);
+ * }
+ *
+ * void onDestroy() {
+ * mSmartspaceSession.unregisterPredictionUpdates()
+ * mSmartspaceSession.destroy();
+ * }
+ *
+ * }</pre>
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSession implements AutoCloseable {
+
+ private static final String TAG = SmartspaceSession.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private final android.app.smartspace.ISmartspaceManager mInterface;
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+ private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
+
+ private final SmartspaceSessionId mSessionId;
+ private final ArrayMap<Callback, CallbackWrapper> mRegisteredCallbacks = new ArrayMap<>();
+ private final IBinder mToken = new Binder();
+
+ /**
+ * Creates a new Smartspace ui client.
+ * <p>
+ * The caller should call {@link SmartspaceSession#destroy()} to dispose the client once it
+ * no longer used.
+ *
+ * @param context the {@link Context} of the user of this {@link SmartspaceSession}.
+ * @param smartspaceConfig the Smartspace context.
+ */
+ // b/177858121 Create weak reference child objects to not leak context.
+ SmartspaceSession(@NonNull Context context, @NonNull SmartspaceConfig smartspaceConfig) {
+ IBinder b = ServiceManager.getService(Context.SMARTSPACE_SERVICE);
+ mInterface = android.app.smartspace.ISmartspaceManager.Stub.asInterface(b);
+ mSessionId = new SmartspaceSessionId(
+ context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId());
+ try {
+ mInterface.createSmartspaceSession(smartspaceConfig, mSessionId, mToken);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to cerate Smartspace session", e);
+ e.rethrowFromSystemServer();
+ }
+
+ mCloseGuard.open("close");
+ }
+
+ /**
+ * Notifies the Smartspace service of a Smartspace target event.
+ *
+ * @param event The {@link SmartspaceTargetEvent} that represents the Smartspace target event.
+ */
+ public void notifySmartspaceEvent(@NonNull SmartspaceTargetEvent event) {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+ try {
+ mInterface.notifySmartspaceEvent(mSessionId, event);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify event", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the smartspace service for an update.
+ */
+ public void requestSmartspaceUpdate() {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+ try {
+ mInterface.requestSmartspaceUpdate(mSessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to request update.", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the smartspace service provide continuous updates of smartspace cards via the
+ * provided callback, until the given callback is unregistered.
+ *
+ * @param callbackExecutor The callback executor to use when calling the callback.
+ * @param callback The Callback to be called when updates of Smartspace targets are
+ * available.
+ */
+ public void registerSmartspaceUpdates(@NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull Callback callback) {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+
+ if (mRegisteredCallbacks.containsKey(callback)) {
+ // Skip if this callback is already registered
+ return;
+ }
+ try {
+ final CallbackWrapper callbackWrapper = new CallbackWrapper(callbackExecutor,
+ callback::onTargetsAvailable);
+ mRegisteredCallbacks.put(callback, callbackWrapper);
+ mInterface.registerSmartspaceUpdates(mSessionId, callbackWrapper);
+ mInterface.requestSmartspaceUpdate(mSessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register for smartspace updates", e);
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Requests the smartspace service to stop providing continuous updates to the provided
+ * callback until the callback is re-registered.
+ *
+ * @see {@link SmartspaceSession#registerSmartspaceUpdates(Executor, Callback)}.
+ *
+ * @param callback The callback to be unregistered.
+ */
+ public void unregisterSmartspaceUpdates(@NonNull Callback callback) {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+
+ if (!mRegisteredCallbacks.containsKey(callback)) {
+ // Skip if this callback was never registered
+ return;
+ }
+ try {
+ final CallbackWrapper callbackWrapper = mRegisteredCallbacks.remove(callback);
+ mInterface.unregisterSmartspaceUpdates(mSessionId, callbackWrapper);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to unregister for smartspace updates", e);
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Destroys the client and unregisters the callback. Any method on this class after this call
+ * will throw {@link IllegalStateException}.
+ */
+ public void destroy() {
+ if (!mIsClosed.getAndSet(true)) {
+ mCloseGuard.close();
+
+ // Do destroy;
+ try {
+ mInterface.destroySmartspaceSession(mSessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify Smartspace target event", e);
+ e.rethrowFromSystemServer();
+ }
+ } else {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ try {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ if (!mIsClosed.get()) {
+ destroy();
+ }
+ } finally {
+ try {
+ super.finalize();
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ finalize();
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ }
+
+ /**
+ * Callback for receiving smartspace updates.
+ */
+ public interface Callback {
+
+ /**
+ * Called when a new set of smartspace targets are available.
+ *
+ * @param targets Sorted list of smartspace targets.
+ */
+ void onTargetsAvailable(@NonNull List<SmartspaceTarget> targets);
+ }
+
+ static class CallbackWrapper extends Stub {
+
+ private final Consumer<List<SmartspaceTarget>> mCallback;
+ private final Executor mExecutor;
+
+ CallbackWrapper(@NonNull Executor callbackExecutor,
+ @NonNull Consumer<List<SmartspaceTarget>> callback) {
+ mCallback = callback;
+ mExecutor = callbackExecutor;
+ }
+
+ @Override
+ public void onResult(ParceledListSlice result) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "CallbackWrapper.onResult result=" + result.getList());
+ }
+ mExecutor.execute(() -> mCallback.accept(result.getList()));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceSessionId.aidl b/core/java/android/app/smartspace/SmartspaceSessionId.aidl
new file mode 100644
index 000000000000..a864412edd65
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceSessionId.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace;
+
+parcelable SmartspaceSessionId; \ No newline at end of file
diff --git a/core/java/android/app/smartspace/SmartspaceSessionId.java b/core/java/android/app/smartspace/SmartspaceSessionId.java
new file mode 100644
index 000000000000..5220c35d7064
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceSessionId.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The id for an Smartspace session. See {@link SmartspaceSession}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSessionId implements Parcelable {
+
+ @NonNull
+ private final String mId;
+
+ @NonNull
+ private final int mUserId;
+
+ /**
+ * Creates a new id for a Smartspace session.
+ *
+ * @hide
+ */
+ public SmartspaceSessionId(@NonNull final String id, @NonNull final int userId) {
+ mId = id;
+ mUserId = userId;
+ }
+
+ private SmartspaceSessionId(Parcel p) {
+ mId = p.readString();
+ mUserId = p.readInt();
+ }
+
+ /**
+ * Returns a {@link String} Id of this sessionId.
+ */
+ @Nullable
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the userId associated with this sessionId.
+ */
+ @NonNull
+ public int getUserId() {
+ return mUserId;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (!getClass().equals(o != null ? o.getClass() : null)) return false;
+
+ SmartspaceSessionId other = (SmartspaceSessionId) o;
+ return mId.equals(other.mId) && mUserId == other.mUserId;
+ }
+
+ @Override
+ public String toString() {
+ return "SmartspaceSessionId{"
+ + "mId='" + mId + '\''
+ + ", mUserId=" + mUserId
+ + '}';
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId, mUserId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeInt(mUserId);
+ }
+
+ public static final @NonNull Creator<SmartspaceSessionId> CREATOR =
+ new Creator<SmartspaceSessionId>() {
+ public SmartspaceSessionId createFromParcel(Parcel parcel) {
+ return new SmartspaceSessionId(parcel);
+ }
+
+ public SmartspaceSessionId[] newArray(int size) {
+ return new SmartspaceSessionId[size];
+ }
+ };
+}
diff --git a/core/java/android/app/smartspace/SmartspaceTarget.aidl b/core/java/android/app/smartspace/SmartspaceTarget.aidl
new file mode 100644
index 000000000000..3442cf294cbb
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceTarget.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace;
+
+parcelable SmartspaceTarget; \ No newline at end of file
diff --git a/core/java/android/app/smartspace/SmartspaceTarget.java b/core/java/android/app/smartspace/SmartspaceTarget.java
new file mode 100644
index 000000000000..ce5040eb0a3e
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceTarget.java
@@ -0,0 +1,660 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.smartspace;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A {@link SmartspaceTarget} is a data class which holds all properties necessary to inflate a
+ * smartspace card. It contains data and related metadata which is supposed to be utilized by
+ * smartspace clients based on their own UI/UX requirements. Some of the properties have
+ * {@link SmartspaceAction} as their type because they can have associated actions.
+ *
+ * <p><b>NOTE: </b>
+ * If {@link mWidgetId} is set, it should be preferred over all other properties.
+ * Else, if {@link mSliceUri} is set, it should be preferred over all other data properties.
+ * Otherwise, the instance should be treated as a data object.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceTarget implements Parcelable {
+
+ /** A unique Id for an instance of {@link SmartspaceTarget}. */
+ @NonNull
+ private final String mSmartspaceTargetId;
+
+ /** A {@link SmartspaceAction} for the header in the Smartspace card. */
+ @Nullable
+ private final SmartspaceAction mHeaderAction;
+
+ /** A {@link SmartspaceAction} for the base action in the Smartspace card. */
+ @Nullable
+ private final SmartspaceAction mBaseAction;
+
+ /** A timestamp indicating when the card was created. */
+ @NonNull
+ private final long mCreationTimeMillis;
+
+ /**
+ * A timestamp indicating when the card should be removed from view, in case the service
+ * disconnects or restarts.
+ */
+ @NonNull
+ private final long mExpiryTimeMillis;
+
+ /** A score assigned to a target. */
+ @NonNull
+ private final float mScore;
+
+ /** A {@link List<SmartspaceAction>} containing all action chips. */
+ @NonNull
+ private final List<SmartspaceAction> mActionChips;
+
+ /** A {@link List<SmartspaceAction>} containing all icons for the grid. */
+ @NonNull
+ private final List<SmartspaceAction> mIconGrid;
+
+ /**
+ * {@link FeatureType} indicating the feature type of this card.
+ *
+ * @see FeatureType
+ */
+ @FeatureType
+ @NonNull
+ private final int mFeatureType;
+
+ /**
+ * Indicates whether the content is sensitive. Certain UI surfaces may choose to skip rendering
+ * real content until the device is unlocked.
+ */
+ @NonNull
+ private final boolean mSensitive;
+
+ /** Indicating if the UI should show this target in its expanded state. */
+ @NonNull
+ private final boolean mShouldShowExpanded;
+
+ /** A Notification key if the target was generated using a notification. */
+ @Nullable
+ private final String mSourceNotificationKey;
+
+ /** {@link ComponentName} for this target. */
+ @NonNull
+ private final ComponentName mComponentName;
+
+ /** {@link UserHandle} for this target. */
+ @NonNull
+ private final UserHandle mUserHandle;
+
+ /** Target Ids of other {@link SmartspaceTarget}s if they are associated with this target. */
+ @Nullable
+ private final String mAssociatedSmartspaceTargetId;
+
+ /** {@link Uri} Slice Uri if this target is a slice. */
+ @Nullable
+ private final Uri mSliceUri;
+
+ /** {@link AppWidgetProviderInfo} if this target is a widget. */
+ @Nullable
+ private final AppWidgetProviderInfo mWidgetId;
+
+ public static final int FEATURE_UNDEFINED = 0;
+ public static final int FEATURE_WEATHER = 1;
+ public static final int FEATURE_CALENDAR = 2;
+ public static final int FEATURE_COMMUTE_TIME = 3;
+ public static final int FEATURE_FLIGHT = 4;
+ public static final int FEATURE_TIPS = 5;
+ public static final int FEATURE_REMINDER = 6;
+ public static final int FEATURE_ALARM = 7;
+ public static final int FEATURE_ONBOARDING = 8;
+ public static final int FEATURE_SPORTS = 9;
+ public static final int FEATURE_WEATHER_ALERT = 10;
+ public static final int FEATURE_CONSENT = 11;
+ public static final int FEATURE_STOCK_PRICE_CHANGE = 12;
+ public static final int FEATURE_SHOPPING_LIST = 13;
+ public static final int FEATURE_LOYALTY_CARD = 14;
+ public static final int FEATURE_MEDIA = 15;
+ public static final int FEATURE_BEDTIME_ROUTINE = 16;
+ public static final int FEATURE_FITNESS_TRACKING = 17;
+ public static final int FEATURE_ETA_MONITORING = 18;
+ public static final int FEATURE_MISSED_CALL = 19;
+ public static final int FEATURE_PACKAGE_TRACKING = 20;
+ public static final int FEATURE_TIMER = 21;
+ public static final int FEATURE_STOPWATCH = 22;
+ public static final int FEATURE_UPCOMING_ALARM = 23;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"FEATURE_"}, value = {
+ FEATURE_UNDEFINED,
+ FEATURE_WEATHER,
+ FEATURE_CALENDAR,
+ FEATURE_COMMUTE_TIME,
+ FEATURE_FLIGHT,
+ FEATURE_TIPS,
+ FEATURE_REMINDER,
+ FEATURE_ALARM,
+ FEATURE_ONBOARDING,
+ FEATURE_SPORTS,
+ FEATURE_WEATHER_ALERT,
+ FEATURE_CONSENT,
+ FEATURE_STOCK_PRICE_CHANGE,
+ FEATURE_SHOPPING_LIST,
+ FEATURE_LOYALTY_CARD,
+ FEATURE_MEDIA,
+ FEATURE_BEDTIME_ROUTINE,
+ FEATURE_FITNESS_TRACKING,
+ FEATURE_ETA_MONITORING,
+ FEATURE_MISSED_CALL,
+ FEATURE_PACKAGE_TRACKING,
+ FEATURE_TIMER,
+ FEATURE_STOPWATCH,
+ FEATURE_UPCOMING_ALARM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FeatureType {
+ }
+
+ private SmartspaceTarget(Parcel in) {
+ this.mSmartspaceTargetId = in.readString();
+ this.mHeaderAction = in.readTypedObject(SmartspaceAction.CREATOR);
+ this.mBaseAction = in.readTypedObject(SmartspaceAction.CREATOR);
+ this.mCreationTimeMillis = in.readLong();
+ this.mExpiryTimeMillis = in.readLong();
+ this.mScore = in.readFloat();
+ this.mActionChips = in.createTypedArrayList(SmartspaceAction.CREATOR);
+ this.mIconGrid = in.createTypedArrayList(SmartspaceAction.CREATOR);
+ this.mFeatureType = in.readInt();
+ this.mSensitive = in.readBoolean();
+ this.mShouldShowExpanded = in.readBoolean();
+ this.mSourceNotificationKey = in.readString();
+ this.mComponentName = in.readTypedObject(ComponentName.CREATOR);
+ this.mUserHandle = in.readTypedObject(UserHandle.CREATOR);
+ this.mAssociatedSmartspaceTargetId = in.readString();
+ this.mSliceUri = in.readTypedObject(Uri.CREATOR);
+ this.mWidgetId = in.readTypedObject(AppWidgetProviderInfo.CREATOR);
+ }
+
+ private SmartspaceTarget(String smartspaceTargetId,
+ SmartspaceAction headerAction, SmartspaceAction baseAction, long creationTimeMillis,
+ long expiryTimeMillis, float score,
+ List<SmartspaceAction> actionChips,
+ List<SmartspaceAction> iconGrid, int featureType, boolean sensitive,
+ boolean shouldShowExpanded, String sourceNotificationKey,
+ ComponentName componentName, UserHandle userHandle,
+ String associatedSmartspaceTargetId, Uri sliceUri,
+ AppWidgetProviderInfo widgetId) {
+ mSmartspaceTargetId = smartspaceTargetId;
+ mHeaderAction = headerAction;
+ mBaseAction = baseAction;
+ mCreationTimeMillis = creationTimeMillis;
+ mExpiryTimeMillis = expiryTimeMillis;
+ mScore = score;
+ mActionChips = actionChips;
+ mIconGrid = iconGrid;
+ mFeatureType = featureType;
+ mSensitive = sensitive;
+ mShouldShowExpanded = shouldShowExpanded;
+ mSourceNotificationKey = sourceNotificationKey;
+ mComponentName = componentName;
+ mUserHandle = userHandle;
+ mAssociatedSmartspaceTargetId = associatedSmartspaceTargetId;
+ mSliceUri = sliceUri;
+ mWidgetId = widgetId;
+ }
+
+ /**
+ * Returns the Id of the target.
+ */
+ @NonNull
+ public String getSmartspaceTargetId() {
+ return mSmartspaceTargetId;
+ }
+
+ /**
+ * Returns the header action of the target.
+ */
+ @Nullable
+ public SmartspaceAction getHeaderAction() {
+ return mHeaderAction;
+ }
+
+ /**
+ * Returns the base action of the target.
+ */
+ @Nullable
+ public SmartspaceAction getBaseAction() {
+ return mBaseAction;
+ }
+
+ /**
+ * Returns the creation time of the target.
+ */
+ @NonNull
+ public long getCreationTimeMillis() {
+ return mCreationTimeMillis;
+ }
+
+ /**
+ * Returns the expiry time of the target.
+ */
+ @NonNull
+ public long getExpiryTimeMillis() {
+ return mExpiryTimeMillis;
+ }
+
+ /**
+ * Returns the score of the target.
+ */
+ @NonNull
+ public float getScore() {
+ return mScore;
+ }
+
+ /**
+ * Return the action chips of the target.
+ */
+ @NonNull
+ public List<SmartspaceAction> getActionChips() {
+ return mActionChips;
+ }
+
+ /**
+ * Return the icons of the target.
+ */
+ @NonNull
+ public List<SmartspaceAction> getIconGrid() {
+ return mIconGrid;
+ }
+
+ /**
+ * Returns the feature type of the target.
+ */
+ @NonNull
+ public int getFeatureType() {
+ return mFeatureType;
+ }
+
+ /**
+ * Returns whether the target is sensitive or not.
+ */
+ @NonNull
+ public boolean isSensitive() {
+ return mSensitive;
+ }
+
+ /**
+ * Returns whether the target should be shown in expanded state.
+ */
+ @NonNull
+ public boolean shouldShowExpanded() {
+ return mShouldShowExpanded;
+ }
+
+ /**
+ * Returns the source notification key of the target.
+ */
+ @Nullable
+ public String getSourceNotificationKey() {
+ return mSourceNotificationKey;
+ }
+
+ /**
+ * Returns the component name of the target.
+ */
+ @NonNull
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ /**
+ * Returns the user handle of the target.
+ */
+ @NonNull
+ public UserHandle getUserHandle() {
+ return mUserHandle;
+ }
+
+ /**
+ * Returns the id of a target associated with this instance.
+ */
+ @Nullable
+ public String getAssociatedSmartspaceTargetId() {
+ return mAssociatedSmartspaceTargetId;
+ }
+
+ /**
+ * Returns the slice uri, if the target is a slice.
+ */
+ @Nullable
+ public Uri getSliceUri() {
+ return mSliceUri;
+ }
+
+ /**
+ * Returns the AppWidgetProviderInfo, if the target is a widget.
+ */
+ @Nullable
+ public AppWidgetProviderInfo getWidgetId() {
+ return mWidgetId;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceTarget> CREATOR = new Creator<SmartspaceTarget>() {
+ @Override
+ public SmartspaceTarget createFromParcel(Parcel source) {
+ return new SmartspaceTarget(source);
+ }
+
+ @Override
+ public SmartspaceTarget[] newArray(int size) {
+ return new SmartspaceTarget[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(this.mSmartspaceTargetId);
+ dest.writeTypedObject(this.mHeaderAction, flags);
+ dest.writeTypedObject(this.mBaseAction, flags);
+ dest.writeLong(this.mCreationTimeMillis);
+ dest.writeLong(this.mExpiryTimeMillis);
+ dest.writeFloat(this.mScore);
+ dest.writeTypedList(this.mActionChips);
+ dest.writeTypedList(this.mIconGrid);
+ dest.writeInt(this.mFeatureType);
+ dest.writeBoolean(this.mSensitive);
+ dest.writeBoolean(this.mShouldShowExpanded);
+ dest.writeString(this.mSourceNotificationKey);
+ dest.writeTypedObject(this.mComponentName, flags);
+ dest.writeTypedObject(this.mUserHandle, flags);
+ dest.writeString(this.mAssociatedSmartspaceTargetId);
+ dest.writeTypedObject(this.mSliceUri, flags);
+ dest.writeTypedObject(this.mWidgetId, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "SmartspaceTarget{"
+ + "mSmartspaceTargetId='" + mSmartspaceTargetId + '\''
+ + ", mHeaderAction=" + mHeaderAction
+ + ", mBaseAction=" + mBaseAction
+ + ", mCreationTimeMillis=" + mCreationTimeMillis
+ + ", mExpiryTimeMillis=" + mExpiryTimeMillis
+ + ", mScore=" + mScore
+ + ", mActionChips=" + mActionChips
+ + ", mIconGrid=" + mIconGrid
+ + ", mFeatureType=" + mFeatureType
+ + ", mSensitive=" + mSensitive
+ + ", mShouldShowExpanded=" + mShouldShowExpanded
+ + ", mSourceNotificationKey='" + mSourceNotificationKey + '\''
+ + ", mComponentName=" + mComponentName
+ + ", mUserHandle=" + mUserHandle
+ + ", mAssociatedSmartspaceTargetId='" + mAssociatedSmartspaceTargetId + '\''
+ + ", mSliceUri=" + mSliceUri
+ + ", mWidgetId=" + mWidgetId
+ + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SmartspaceTarget that = (SmartspaceTarget) o;
+ return mCreationTimeMillis == that.mCreationTimeMillis
+ && mExpiryTimeMillis == that.mExpiryTimeMillis
+ && Float.compare(that.mScore, mScore) == 0
+ && mFeatureType == that.mFeatureType
+ && mSensitive == that.mSensitive
+ && mShouldShowExpanded == that.mShouldShowExpanded
+ && mSmartspaceTargetId.equals(that.mSmartspaceTargetId)
+ && Objects.equals(mHeaderAction, that.mHeaderAction)
+ && Objects.equals(mBaseAction, that.mBaseAction)
+ && Objects.equals(mActionChips, that.mActionChips)
+ && Objects.equals(mIconGrid, that.mIconGrid)
+ && Objects.equals(mSourceNotificationKey, that.mSourceNotificationKey)
+ && mComponentName.equals(that.mComponentName)
+ && mUserHandle.equals(that.mUserHandle)
+ && Objects.equals(mAssociatedSmartspaceTargetId,
+ that.mAssociatedSmartspaceTargetId)
+ && Objects.equals(mSliceUri, that.mSliceUri)
+ && Objects.equals(mWidgetId, that.mWidgetId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSmartspaceTargetId, mHeaderAction, mBaseAction, mCreationTimeMillis,
+ mExpiryTimeMillis, mScore, mActionChips, mIconGrid, mFeatureType, mSensitive,
+ mShouldShowExpanded, mSourceNotificationKey, mComponentName, mUserHandle,
+ mAssociatedSmartspaceTargetId, mSliceUri, mWidgetId);
+ }
+
+ /**
+ * A builder for {@link SmartspaceTarget} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ private final String mSmartspaceTargetId;
+ private SmartspaceAction mHeaderAction;
+ private SmartspaceAction mBaseAction;
+ private long mCreationTimeMillis;
+ private long mExpiryTimeMillis;
+ private float mScore;
+ private List<SmartspaceAction> mActionChips = new ArrayList<>();
+ private List<SmartspaceAction> mIconGrid = new ArrayList<>();
+ private int mFeatureType;
+ private boolean mSensitive;
+ private boolean mShouldShowExpanded;
+ private String mSourceNotificationKey;
+ private final ComponentName mComponentName;
+ private final UserHandle mUserHandle;
+ private String mAssociatedSmartspaceTargetId;
+ private Uri mSliceUri;
+ private AppWidgetProviderInfo mWidgetId;
+
+ /**
+ * A builder for {@link SmartspaceTarget}.
+ *
+ * @param smartspaceTargetId the id of this target
+ * @param componentName the componentName of this target
+ * @param userHandle the userHandle of this target
+ */
+ public Builder(@NonNull String smartspaceTargetId,
+ @NonNull ComponentName componentName, @NonNull UserHandle userHandle) {
+ this.mSmartspaceTargetId = smartspaceTargetId;
+ this.mComponentName = componentName;
+ this.mUserHandle = userHandle;
+ }
+
+ /**
+ * Sets the header action.
+ */
+ @NonNull
+ public Builder setHeaderAction(@NonNull SmartspaceAction headerAction) {
+ this.mHeaderAction = headerAction;
+ return this;
+ }
+
+ /**
+ * Sets the base action.
+ */
+ @NonNull
+ public Builder setBaseAction(@NonNull SmartspaceAction baseAction) {
+ this.mBaseAction = baseAction;
+ return this;
+ }
+
+ /**
+ * Sets the creation time.
+ */
+ @NonNull
+ public Builder setCreationTimeMillis(@NonNull long creationTimeMillis) {
+ this.mCreationTimeMillis = creationTimeMillis;
+ return this;
+ }
+
+ /**
+ * Sets the expiration time.
+ */
+ @NonNull
+ public Builder setExpiryTimeMillis(@NonNull long expiryTimeMillis) {
+ this.mExpiryTimeMillis = expiryTimeMillis;
+ return this;
+ }
+
+ /**
+ * Sets the score.
+ */
+ @NonNull
+ public Builder setScore(@NonNull float score) {
+ this.mScore = score;
+ return this;
+ }
+
+ /**
+ * Sets the action chips.
+ */
+ @NonNull
+ public Builder setActionChips(@NonNull List<SmartspaceAction> actionChips) {
+ this.mActionChips = actionChips;
+ return this;
+ }
+
+ /**
+ * Sets the icon grid.
+ */
+ @NonNull
+ public Builder setIconGrid(@NonNull List<SmartspaceAction> iconGrid) {
+ this.mIconGrid = iconGrid;
+ return this;
+ }
+
+ /**
+ * Sets the feature type.
+ */
+ @NonNull
+ public Builder setFeatureType(@NonNull int featureType) {
+ this.mFeatureType = featureType;
+ return this;
+ }
+
+ /**
+ * Sets whether the contents are sensitive.
+ */
+ @NonNull
+ public Builder setSensitive(@NonNull boolean sensitive) {
+ this.mSensitive = sensitive;
+ return this;
+ }
+
+ /**
+ * Sets whether to show the card as expanded.
+ */
+ @NonNull
+ public Builder setShouldShowExpanded(@NonNull boolean shouldShowExpanded) {
+ this.mShouldShowExpanded = shouldShowExpanded;
+ return this;
+ }
+
+ /**
+ * Sets the source notification key.
+ */
+ @NonNull
+ public Builder setSourceNotificationKey(@NonNull String sourceNotificationKey) {
+ this.mSourceNotificationKey = sourceNotificationKey;
+ return this;
+ }
+
+ /**
+ * Sets the associated smartspace target id.
+ */
+ @NonNull
+ public Builder setAssociatedSmartspaceTargetId(
+ @NonNull String associatedSmartspaceTargetId) {
+ this.mAssociatedSmartspaceTargetId = associatedSmartspaceTargetId;
+ return this;
+ }
+
+ /**
+ * Sets the slice uri.
+ *
+ * <p><b>NOTE: </b> If {@link mWidgetId} is also set, {@link mSliceUri} should be ignored.
+ */
+ @NonNull
+ public Builder setSliceUri(@NonNull Uri sliceUri) {
+ this.mSliceUri = sliceUri;
+ return this;
+ }
+
+ /**
+ * Sets the widget id.
+ *
+ * <p><b>NOTE: </b> If {@link mWidgetId} is set, all other @Nullable params should be
+ * ignored.
+ */
+ @NonNull
+ public Builder setWidgetId(@NonNull AppWidgetProviderInfo widgetId) {
+ this.mWidgetId = widgetId;
+ return this;
+ }
+
+ /**
+ * Builds a new {@link SmartspaceTarget}.
+ *
+ * @throws IllegalStateException when non null fields are set as null.
+ */
+ @NonNull
+ public SmartspaceTarget build() {
+ if (mSmartspaceTargetId == null
+ || mComponentName == null
+ || mUserHandle == null) {
+ throw new IllegalStateException("Please assign a value to all @NonNull args.");
+ }
+ return new SmartspaceTarget(mSmartspaceTargetId,
+ mHeaderAction, mBaseAction, mCreationTimeMillis, mExpiryTimeMillis, mScore,
+ mActionChips, mIconGrid, mFeatureType, mSensitive, mShouldShowExpanded,
+ mSourceNotificationKey, mComponentName, mUserHandle,
+ mAssociatedSmartspaceTargetId, mSliceUri, mWidgetId);
+ }
+ }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.aidl b/core/java/android/app/smartspace/SmartspaceTargetEvent.aidl
new file mode 100644
index 000000000000..e797a9b7e42c
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace;
+
+parcelable SmartspaceTargetEvent;
diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.java b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
new file mode 100644
index 000000000000..1e0653d67ace
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.smartspace;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A representation of a smartspace event.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceTargetEvent implements Parcelable {
+
+ /**
+ * User interacted with the target.
+ */
+ public static final int EVENT_TARGET_INTERACTION = 1;
+
+ /**
+ * Smartspace target was brought into view.
+ */
+ public static final int EVENT_TARGET_IN_VIEW = 2;
+ /**
+ * Smartspace target went out of view.
+ */
+ public static final int EVENT_TARGET_OUT_OF_VIEW = 3;
+ /**
+ * A dismiss action was issued by the user.
+ */
+ public static final int EVENT_TARGET_DISMISS = 4;
+ /**
+ * A block action was issued by the user.
+ */
+ public static final int EVENT_TARGET_BLOCK = 5;
+ /**
+ * The Ui surface came into view.
+ */
+ public static final int EVENT_UI_SURFACE_IN_VIEW = 6;
+ /**
+ * The Ui surface went out of view.
+ */
+ public static final int EVENT_UI_SURFACE_OUT_OF_VIEW = 7;
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SmartspaceTargetEvent> CREATOR =
+ new Creator<SmartspaceTargetEvent>() {
+ public SmartspaceTargetEvent createFromParcel(Parcel parcel) {
+ return new SmartspaceTargetEvent(parcel);
+ }
+
+ public SmartspaceTargetEvent[] newArray(int size) {
+ return new SmartspaceTargetEvent[size];
+ }
+ };
+
+ @Nullable
+ private final SmartspaceTarget mSmartspaceTarget;
+
+ @Nullable
+ private final String mSmartspaceActionId;
+
+ @EventType
+ private final int mEventType;
+
+ private SmartspaceTargetEvent(@Nullable SmartspaceTarget smartspaceTarget,
+ @Nullable String smartspaceActionId,
+ @EventType int eventType) {
+ mSmartspaceTarget = smartspaceTarget;
+ mSmartspaceActionId = smartspaceActionId;
+ mEventType = eventType;
+ }
+
+ private SmartspaceTargetEvent(Parcel parcel) {
+ mSmartspaceTarget = parcel.readParcelable(null);
+ mSmartspaceActionId = parcel.readString();
+ mEventType = parcel.readInt();
+ }
+
+ /**
+ * Get the {@link SmartspaceTarget} associated with this event.
+ */
+ @Nullable
+ public SmartspaceTarget getSmartspaceTarget() {
+ return mSmartspaceTarget;
+ }
+
+ /**
+ * Get the action id of the Smartspace Action associated with this event.
+ */
+ @Nullable
+ public String getSmartspaceActionId() {
+ return mSmartspaceActionId;
+ }
+
+ /**
+ * Get the {@link EventType} of this event.
+ */
+ @NonNull
+ @EventType
+ public int getEventType() {
+ return mEventType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mSmartspaceTarget, flags);
+ dest.writeString(mSmartspaceActionId);
+ dest.writeInt(mEventType);
+ }
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"EVENT_"}, value = {
+ EVENT_TARGET_INTERACTION,
+ EVENT_TARGET_IN_VIEW,
+ EVENT_TARGET_OUT_OF_VIEW,
+ EVENT_TARGET_DISMISS,
+ EVENT_TARGET_BLOCK,
+ EVENT_UI_SURFACE_IN_VIEW,
+ EVENT_UI_SURFACE_OUT_OF_VIEW
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {
+ }
+
+ /**
+ * A builder for {@link SmartspaceTargetEvent}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ @EventType
+ private final int mEventType;
+ @Nullable
+ private SmartspaceTarget mSmartspaceTarget;
+ @Nullable
+ private String mSmartspaceActionId;
+
+ /**
+ * A builder for {@link SmartspaceTargetEvent}.
+ */
+ public Builder(@EventType int eventType) {
+ mEventType = eventType;
+ }
+
+ /**
+ * Sets the SmartspaceTarget for this event.
+ */
+ @NonNull
+ public Builder setSmartspaceTarget(@NonNull SmartspaceTarget smartspaceTarget) {
+ mSmartspaceTarget = smartspaceTarget;
+ return this;
+ }
+
+ /**
+ * Sets the Smartspace action id for this event.
+ */
+ @NonNull
+ public Builder setSmartspaceActionId(@NonNull String smartspaceActionId) {
+ mSmartspaceActionId = smartspaceActionId;
+ return this;
+ }
+
+ /**
+ * Builds a new {@link SmartspaceTargetEvent} instance.
+ */
+ @NonNull
+ public SmartspaceTargetEvent build() {
+ return new SmartspaceTargetEvent(mSmartspaceTarget, mSmartspaceActionId, mEventType);
+ }
+ }
+}
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 93d96d074db8..e96e22c4764a 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -406,6 +406,14 @@ public class AppWidgetProviderInfo implements Parcelable {
return new UserHandle(UserHandle.getUserId(providerInfo.applicationInfo.uid));
}
+ /**
+ * Returns the broadcast receiver that is providing this widget.
+ */
+ @NonNull
+ public ActivityInfo getProviderInfo() {
+ return providerInfo;
+ }
+
@Override
@SuppressWarnings("deprecation")
public void writeToParcel(Parcel out, int flags) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 125b5ff625b2..6d42703f2730 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4625,6 +4625,18 @@ public abstract class Context {
public static final String SEARCH_UI_SERVICE = "search_ui";
/**
+ * Used for getting the smartspace service.
+ *
+ * <p><b>NOTE: </b> this service is optional; callers of
+ * {@code Context.getSystemServiceName(SMARTSPACE_SERVICE)} should check for {@code null}.
+ *
+ * @hide
+ * @see #getSystemService(String)
+ */
+ @SystemApi
+ public static final String SMARTSPACE_SERVICE = "smartspace";
+
+ /**
* Use with {@link #getSystemService(String)} to access the
* {@link com.android.server.voiceinteraction.SoundTriggerService}.
*
@@ -6154,6 +6166,7 @@ public abstract class Context {
@UiContext
@NonNull
public Context createWindowContext(@NonNull Display display, @WindowType int type,
+ @SuppressLint("NullableCollection")
@Nullable Bundle options) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 82d7b6361738..16e720e3794c 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.annotation.FloatRange;
+import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -32,8 +33,6 @@ import android.util.DisplayMetrics;
* and badged icon for the activity.
*/
public class LauncherActivityInfo {
- private static final String TAG = "LauncherActivityInfo";
-
private final PackageManager mPm;
private UserHandle mUser;
private final LauncherActivityInfoInternal mInternal;
@@ -81,7 +80,7 @@ public class LauncherActivityInfo {
*/
public CharSequence getLabel() {
// TODO: Go through LauncherAppsService
- return mInternal.getActivityInfo().loadLabel(mPm);
+ return getActivityInfo().loadLabel(mPm);
}
/**
@@ -101,20 +100,20 @@ public class LauncherActivityInfo {
*/
public Drawable getIcon(int density) {
// TODO: Go through LauncherAppsService
- final int iconRes = mInternal.getActivityInfo().getIconResource();
+ final int iconRes = getActivityInfo().getIconResource();
Drawable icon = null;
// Get the preferred density icon from the app's resources
if (density != 0 && iconRes != 0) {
try {
final Resources resources = mPm.getResourcesForApplication(
- mInternal.getActivityInfo().applicationInfo);
+ getActivityInfo().applicationInfo);
icon = resources.getDrawableForDensity(iconRes, density);
} catch (NameNotFoundException | Resources.NotFoundException exc) {
}
}
// Get the default density icon
if (icon == null) {
- icon = mInternal.getActivityInfo().loadIcon(mPm);
+ icon = getActivityInfo().loadIcon(mPm);
}
return icon;
}
@@ -126,25 +125,25 @@ public class LauncherActivityInfo {
* @hide remove before shipping
*/
public int getApplicationFlags() {
- return mInternal.getActivityInfo().flags;
+ return getActivityInfo().flags;
}
/**
* Returns the ActivityInfo of the activity.
*
* @return Activity Info
- * @hide
*/
+ @NonNull
public ActivityInfo getActivityInfo() {
return mInternal.getActivityInfo();
}
/**
- * Returns the application info for the appliction this activity belongs to.
+ * Returns the application info for the application this activity belongs to.
* @return
*/
public ApplicationInfo getApplicationInfo() {
- return mInternal.getActivityInfo().applicationInfo;
+ return getActivityInfo().applicationInfo;
}
/**
@@ -155,7 +154,7 @@ public class LauncherActivityInfo {
public long getFirstInstallTime() {
try {
// TODO: Go through LauncherAppsService
- return mPm.getPackageInfo(mInternal.getActivityInfo().packageName,
+ return mPm.getPackageInfo(getActivityInfo().packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES).firstInstallTime;
} catch (NameNotFoundException nnfe) {
// Sorry, can't find package
@@ -164,11 +163,11 @@ public class LauncherActivityInfo {
}
/**
- * Returns the name for the acitivty from android:name in the manifest.
- * @return the name from android:name for the acitivity.
+ * Returns the name for the activity from android:name in the manifest.
+ * @return the name from android:name for the activity.
*/
public String getName() {
- return mInternal.getActivityInfo().name;
+ return getActivityInfo().name;
}
/**
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index f9913067d238..e8ef077fedc8 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -222,7 +222,7 @@ public class PackageInfo implements Parcelable {
* &lt;attribution&gt;} tags included under &lt;manifest&gt;, or null if there were none. This
* is only filled if the flag {@link PackageManager#GET_ATTRIBUTIONS} was set.
*/
- @SuppressWarnings("ArrayReturn")
+ @SuppressWarnings({"ArrayReturn", "NullableCollection"})
public @Nullable Attribution[] attributions;
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9ae9c25c8c08..72fb1cae3871 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -54,6 +54,7 @@ import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
@@ -124,6 +125,19 @@ public abstract class PackageManager {
}
/**
+ * &lt;application&gt; level {@link android.content.pm.PackageManager.Property} tag specifying
+ * the XML resource ID containing an application's media capabilities XML file
+ *
+ * For example:
+ * &lt;application&gt;
+ * &lt;property android:name="android.media.PROPERTY_MEDIA_CAPABILITIES"
+ * android:resource="@xml/media_capabilities"&gt;
+ * &lt;application&gt;
+ */
+ public static final String PROPERTY_MEDIA_CAPABILITIES =
+ "android.media.PROPERTY_MEDIA_CAPABILITIES";
+
+ /**
* A property value set within the manifest.
* <p>
* The value of a property will only have a single type, as defined by
@@ -6703,6 +6717,22 @@ public abstract class PackageManager {
throws NameNotFoundException;
/**
+ * Retrieve the resources for an application for the provided configuration.
+ *
+ * @param app Information about the desired application.
+ * @param configuration Overridden configuration when loading the Resources
+ *
+ * @return Returns the application's Resources.
+ * @throws NameNotFoundException Thrown if the resources for the given
+ * application could not be loaded (most likely because it was uninstalled).
+ */
+ @NonNull
+ public Resources getResourcesForApplication(@NonNull ApplicationInfo app, @Nullable
+ Configuration configuration) throws NameNotFoundException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Retrieve the resources associated with an application. Given the full
* package name of an application, retrieves the information about it and
* calls getResources() to return its application's resources. If the
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index 02fb06b808a2..bce4b872b8a6 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -256,6 +256,8 @@ public class Signature implements Parcelable {
try {
if (obj != null) {
Signature other = (Signature)obj;
+ // Note, some classes, such as PackageParser.SigningDetails, rely on equals
+ // only comparing the mSignature arrays without the flags.
return this == other || Arrays.equals(mSignature, other.mSignature);
}
} catch (ClassCastException e) {
@@ -268,6 +270,8 @@ public class Signature implements Parcelable {
if (mHaveHashCode) {
return mHashCode;
}
+ // Note, similar to equals some classes rely on the hash code not including
+ // the flags for Set membership checks.
mHashCode = Arrays.hashCode(mSignature);
mHaveHashCode = true;
return mHashCode;
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index f6edb2edc5ff..abf694f9742e 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -89,6 +89,11 @@ public class CompatibilityInfo implements Parcelable {
private static final int NEEDS_COMPAT_RES = 16;
/**
+ * Set if the application needs to be forcibly downscaled
+ */
+ private static final int HAS_OVERRIDE_SCALING = 32;
+
+ /**
* The effective screen density we have selected for this application.
*/
public final int applicationDensity;
@@ -107,6 +112,11 @@ public class CompatibilityInfo implements Parcelable {
@UnsupportedAppUsage
public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw,
boolean forceCompat) {
+ this(appInfo, screenLayout, sw, forceCompat, 1f);
+ }
+
+ public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw,
+ boolean forceCompat, float overrideScale) {
int compatFlags = 0;
if (appInfo.targetSdkVersion < VERSION_CODES.O) {
@@ -241,7 +251,12 @@ public class CompatibilityInfo implements Parcelable {
compatFlags |= NEVER_NEEDS_COMPAT;
}
- if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
+ if (overrideScale != 1.0f) {
+ applicationDensity = DisplayMetrics.DENSITY_DEFAULT;
+ applicationScale = overrideScale;
+ applicationInvertedScale = 1.0f / overrideScale;
+ compatFlags |= HAS_OVERRIDE_SCALING;
+ } else if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
applicationDensity = DisplayMetrics.DENSITY_DEVICE;
applicationScale = 1.0f;
applicationInvertedScale = 1.0f;
@@ -277,7 +292,7 @@ public class CompatibilityInfo implements Parcelable {
*/
@UnsupportedAppUsage
public boolean isScalingRequired() {
- return (mCompatibilityFlags&SCALING_REQUIRED) != 0;
+ return (mCompatibilityFlags & (SCALING_REQUIRED | HAS_OVERRIDE_SCALING)) != 0;
}
@UnsupportedAppUsage
@@ -303,7 +318,7 @@ public class CompatibilityInfo implements Parcelable {
*/
@UnsupportedAppUsage
public Translator getTranslator() {
- return isScalingRequired() ? new Translator() : null;
+ return (mCompatibilityFlags & SCALING_REQUIRED) != 0 ? new Translator() : null;
}
/**
@@ -504,6 +519,16 @@ public class CompatibilityInfo implements Parcelable {
if (isScalingRequired()) {
float invertedRatio = applicationInvertedScale;
inoutConfig.densityDpi = (int)((inoutConfig.densityDpi * invertedRatio) + .5f);
+ inoutConfig.screenWidthDp = (int) ((inoutConfig.screenWidthDp * invertedRatio) + .5f);
+ inoutConfig.screenHeightDp = (int) ((inoutConfig.screenHeightDp * invertedRatio) + .5f);
+ inoutConfig.smallestScreenWidthDp =
+ (int) ((inoutConfig.smallestScreenWidthDp * invertedRatio) + .5f);
+ inoutConfig.windowConfiguration.getMaxBounds().scale(invertedRatio);
+ inoutConfig.windowConfiguration.getBounds().scale(invertedRatio);
+ final Rect appBounds = inoutConfig.windowConfiguration.getAppBounds();
+ if (appBounds != null) {
+ appBounds.scale(invertedRatio);
+ }
}
}
diff --git a/core/java/android/hardware/Battery.java b/core/java/android/hardware/Battery.java
new file mode 100644
index 000000000000..24c8d76a9813
--- /dev/null
+++ b/core/java/android/hardware/Battery.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.os.BatteryManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The Battery class is a representation of a single battery on a device.
+ */
+public abstract class Battery {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_UNKNOWN,
+ STATUS_CHARGING,
+ STATUS_DISCHARGING,
+ STATUS_NOT_CHARGING,
+ STATUS_FULL
+ })
+ public @interface BatteryStatus {
+ }
+
+ /** Battery status is unknown. */
+ public static final int STATUS_UNKNOWN = BatteryManager.BATTERY_STATUS_UNKNOWN;
+ /** Battery is charging. */
+ public static final int STATUS_CHARGING = BatteryManager.BATTERY_STATUS_CHARGING;
+ /** Battery is discharging. */
+ public static final int STATUS_DISCHARGING = BatteryManager.BATTERY_STATUS_DISCHARGING;
+ /** Battery is connected to power but not charging. */
+ public static final int STATUS_NOT_CHARGING = BatteryManager.BATTERY_STATUS_NOT_CHARGING;
+ /** Battery is full. */
+ public static final int STATUS_FULL = BatteryManager.BATTERY_STATUS_FULL;
+
+ /**
+ * Check whether the hardware has a battery.
+ *
+ * @return True if the hardware has a battery, else false.
+ */
+ public abstract boolean hasBattery();
+
+ /**
+ * Get the battery status.
+ *
+ * @return the battery status.
+ */
+ public abstract @BatteryStatus int getStatus();
+
+ /**
+ * Get remaining battery capacity as float percentage [0.0f, 1.0f] of total capacity
+ * Returns -1 when battery capacity can't be read.
+ *
+ * @return the battery capacity.
+ */
+ public abstract @FloatRange(from = -1.0f, to = 1.0f) float getCapacity();
+}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index b39df4d2b6bf..c69c47f80d01 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -93,6 +93,10 @@ interface IInputManager {
int[] getVibratorIds(int deviceId);
boolean isVibrating(int deviceId);
+ // Input device battery query.
+ int getBatteryStatus(int deviceId);
+ int getBatteryCapacity(int deviceId);
+
void setPointerIconType(int typeId);
void setCustomPointerIcon(in PointerIcon icon);
diff --git a/core/java/android/hardware/input/InputDeviceBattery.java b/core/java/android/hardware/input/InputDeviceBattery.java
new file mode 100644
index 000000000000..0fe124e2c0ce
--- /dev/null
+++ b/core/java/android/hardware/input/InputDeviceBattery.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
+import static android.os.IInputConstants.INVALID_BATTERY_CAPACITY;
+
+import android.hardware.Battery;
+
+/**
+ * Battery implementation for input devices.
+ *
+ * @hide
+ */
+public final class InputDeviceBattery extends Battery {
+ private static final float NULL_BATTERY_CAPACITY = -1.0f;
+
+ private final InputManager mInputManager;
+ private final int mDeviceId;
+ private final boolean mHasBattery;
+
+ InputDeviceBattery(InputManager inputManager, int deviceId, boolean hasBattery) {
+ mInputManager = inputManager;
+ mDeviceId = deviceId;
+ mHasBattery = hasBattery;
+ }
+
+ @Override
+ public boolean hasBattery() {
+ return mHasBattery;
+ }
+
+ @Override
+ public int getStatus() {
+ if (!mHasBattery) {
+ return BATTERY_STATUS_UNKNOWN;
+ }
+ return mInputManager.getBatteryStatus(mDeviceId);
+ }
+
+ @Override
+ public float getCapacity() {
+ if (mHasBattery) {
+ int capacity = mInputManager.getBatteryCapacity(mDeviceId);
+ if (capacity != INVALID_BATTERY_CAPACITY) {
+ return (float) capacity / 100.0f;
+ }
+ }
+ return NULL_BATTERY_CAPACITY;
+ }
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index e63dc112233f..185c59d8ccfc 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1245,6 +1245,32 @@ public final class InputManager {
}
/**
+ * Get the battery status of the input device
+ * @param deviceId The input device ID
+ * @hide
+ */
+ public int getBatteryStatus(int deviceId) {
+ try {
+ return mIm.getBatteryStatus(deviceId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the remaining battery capacity of the input device
+ * @param deviceId The input device ID
+ * @hide
+ */
+ public int getBatteryCapacity(int deviceId) {
+ try {
+ return mIm.getBatteryCapacity(deviceId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Add a runtime association between the input port and the display port. This overrides any
* static associations.
* @param inputPort The port of the input device.
@@ -1471,6 +1497,15 @@ public final class InputManager {
}
/**
+ * Gets a battery object associated with an input device, assuming it has one.
+ * @return The battery, never null.
+ * @hide
+ */
+ public InputDeviceBattery getInputDeviceBattery(int deviceId, boolean hasBattery) {
+ return new InputDeviceBattery(this, deviceId, hasBattery);
+ }
+
+ /**
* Listens for changes in input devices.
*/
public interface InputDeviceListener {
diff --git a/core/java/android/hardware/location/ContextHubClientCallback.java b/core/java/android/hardware/location/ContextHubClientCallback.java
index 20fa7538e2d5..b31b85fdabf6 100644
--- a/core/java/android/hardware/location/ContextHubClientCallback.java
+++ b/core/java/android/hardware/location/ContextHubClientCallback.java
@@ -15,6 +15,7 @@
*/
package android.hardware.location;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import java.util.concurrent.Executor;
@@ -101,4 +102,34 @@ public class ContextHubClientCallback {
* @param nanoAppId the ID of the nanoapp that had been disabled
*/
public void onNanoAppDisabled(ContextHubClient client, long nanoAppId) {}
+
+ /**
+ * Callback invoked when a {@link ContextHubClient}'s authorization to communicate with a
+ * nanoapp changes. This typically happens as a result of the app that created the
+ * {@link ContextHubClient} gaining or losing the permissions required to communicate with a
+ * nanoapp.
+ *
+ * An example of the connection callbacks looks like:
+ * 1) {@link ContextHubClient} sends message to nanoapp and holds required permissions
+ * 2) {@link ContextHubClient} loses required permissions
+ * 3) Callback invoked with the nanoapp ID and
+ * {@link ContextHubManager#AUTHORIZATION_DENIED_GRACE_PERIOD}
+ * 4) {@link ContextHubClient} performs any cleanup required with the nanoapp
+ * 5) Callback invoked with the nanoapp ID and {@link ContextHubManager#AUTHORIZATION_DENIED}.
+ * At this point, any further attempts of communication between the nanoapp and the
+ * {@link ContextHubClient} will be dropped by the contexthub along with
+ * {@link ContextHubManager#AUTHORIZATION_DENIED} being sent. The {@link ContextHubClient}
+ * should assume no communciation can happen again until
+ * {@link ContextHubManager#AUTHORIZATION_GRANTED} is received.
+ *
+ * @param client the client that is associated with this callback
+ * @param nanoAppId the ID of the nanoapp associated with the new
+ * authorization state
+ * @param authorization the authorization state denoting the ability of the
+ * client to communicate with the nanoapp
+ */
+ public void onClientAuthorizationChanged(
+ @NonNull ContextHubClient client,
+ long nanoAppId,
+ @ContextHubManager.AuthorizationState int authorization) {}
}
diff --git a/core/java/android/hardware/location/ContextHubIntentEvent.java b/core/java/android/hardware/location/ContextHubIntentEvent.java
index 917f62bc6cbf..3e8f4219a485 100644
--- a/core/java/android/hardware/location/ContextHubIntentEvent.java
+++ b/core/java/android/hardware/location/ContextHubIntentEvent.java
@@ -43,39 +43,45 @@ public class ContextHubIntentEvent {
private final int mNanoAppAbortCode;
+ private final int mClientAuthorizationState;
+
private ContextHubIntentEvent(
@NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
- long nanoAppId, NanoAppMessage nanoAppMessage, int nanoAppAbortCode) {
+ long nanoAppId, NanoAppMessage nanoAppMessage, int nanoAppAbortCode,
+ @ContextHubManager.AuthorizationState int clientAuthorizationState) {
mContextHubInfo = contextHubInfo;
mEventType = eventType;
mNanoAppId = nanoAppId;
mNanoAppMessage = nanoAppMessage;
mNanoAppAbortCode = nanoAppAbortCode;
+ mClientAuthorizationState = clientAuthorizationState;
}
private ContextHubIntentEvent(
@NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType) {
this(contextHubInfo, eventType, -1 /* nanoAppId */, null /* nanoAppMessage */,
- -1 /* nanoAppAbortCode */);
+ -1 /* nanoAppAbortCode */, 0 /* clientAuthorizationState */);
}
private ContextHubIntentEvent(
@NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
long nanoAppId) {
this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */,
- -1 /* nanoAppAbortCode */);
+ -1 /* nanoAppAbortCode */, 0 /* clientAuthorizationState */);
}
private ContextHubIntentEvent(
@NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
long nanoAppId, @NonNull NanoAppMessage nanoAppMessage) {
- this(contextHubInfo, eventType, nanoAppId, nanoAppMessage, -1 /* nanoAppAbortCode */);
+ this(contextHubInfo, eventType, nanoAppId, nanoAppMessage, -1 /* nanoAppAbortCode */,
+ 0 /* clientAuthorizationState */);
}
private ContextHubIntentEvent(
@NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
long nanoAppId, int nanoAppAbortCode) {
- this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */, nanoAppAbortCode);
+ this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */, nanoAppAbortCode,
+ 0 /* clientAuthorizationState */);
}
/**
@@ -105,7 +111,8 @@ public class ContextHubIntentEvent {
case ContextHubManager.EVENT_NANOAPP_ENABLED:
case ContextHubManager.EVENT_NANOAPP_DISABLED:
case ContextHubManager.EVENT_NANOAPP_ABORTED:
- case ContextHubManager.EVENT_NANOAPP_MESSAGE: // fall through
+ case ContextHubManager.EVENT_NANOAPP_MESSAGE:
+ case ContextHubManager.EVENT_CLIENT_AUTHORIZATION: // fall through
long nanoAppId = getLongExtraOrThrow(intent, ContextHubManager.EXTRA_NANOAPP_ID);
if (eventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
hasExtraOrThrow(intent, ContextHubManager.EXTRA_MESSAGE);
@@ -120,6 +127,11 @@ public class ContextHubIntentEvent {
int nanoAppAbortCode = getIntExtraOrThrow(
intent, ContextHubManager.EXTRA_NANOAPP_ABORT_CODE);
event = new ContextHubIntentEvent(info, eventType, nanoAppId, nanoAppAbortCode);
+ } else if (eventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
+ int authState = getIntExtraOrThrow(
+ intent, ContextHubManager.EXTRA_CLIENT_AUTHORIZATION_STATE);
+ event = new ContextHubIntentEvent(info, eventType, nanoAppId,
+ null /* nanoAppMessage */, -1 /* nanoAppAbortCode */, authState);
} else {
event = new ContextHubIntentEvent(info, eventType, nanoAppId);
}
@@ -192,6 +204,21 @@ public class ContextHubIntentEvent {
return mNanoAppMessage;
}
+ /**
+ * @return the client authorization state
+ *
+ * @throws UnsupportedOperationException if this was not a client authorization state event
+ */
+ @ContextHubManager.AuthorizationState
+ public int getClientAuthorizationState() {
+ if (mEventType != ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
+ throw new UnsupportedOperationException(
+ "Cannot invoke getClientAuthorizationState() on non-authorization event: "
+ + mEventType);
+ }
+ return mClientAuthorizationState;
+ }
+
@NonNull
@Override
public String toString() {
@@ -207,6 +234,9 @@ public class ContextHubIntentEvent {
if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
out += ", nanoAppMessage = " + mNanoAppMessage;
}
+ if (mEventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
+ out += ", clientAuthState = " + mClientAuthorizationState;
+ }
return out + "]";
}
@@ -233,6 +263,9 @@ public class ContextHubIntentEvent {
if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
isEqual &= other.getNanoAppMessage().equals(mNanoAppMessage);
}
+ if (mEventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
+ isEqual &= other.getClientAuthorizationState() == mClientAuthorizationState;
+ }
} catch (UnsupportedOperationException e) {
isEqual = false;
}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index d44480796d75..ebb3021bf083 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -59,6 +59,12 @@ public final class ContextHubManager {
private static final String TAG = "ContextHubManager";
/**
+ * An extra of type int describing the client's authorization state.
+ */
+ public static final String EXTRA_CLIENT_AUTHORIZATION_STATE =
+ "android.hardware.location.extra.CLIENT_AUTHORIZATION_STATE";
+
+ /**
* An extra of type {@link ContextHubInfo} describing the source of the event.
*/
public static final String EXTRA_CONTEXT_HUB_INFO =
@@ -86,6 +92,42 @@ public final class ContextHubManager {
public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
/**
+ * Constants describing if a {@link ContextHubClient} and a {@link NanoApp} are authorized to
+ * communicate.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "AUTHORIZATION_" }, value = {
+ AUTHORIZATION_DENIED,
+ AUTHORIZATION_DENIED_GRACE_PERIOD,
+ AUTHORIZATION_GRANTED,
+ })
+ public @interface AuthorizationState { }
+
+ /**
+ * Indicates that the {@link ContextHubClient} can no longer communicate with a nanoapp. If the
+ * {@link ContextHubClient} attempts to send messages to the nanoapp, it will continue to
+ * receive this authorization state if the connection is still closed.
+ */
+ public static final int AUTHORIZATION_DENIED = 0;
+
+ /**
+ * Indicates the {@link ContextHubClient} will soon lose its authorization to communicate with a
+ * nanoapp. The {@link ContextHubClient} must perform any cleanup with the nanoapp as soon as
+ * possible.
+ *
+ * Note that the time between this state event and {@link AUTHORIZATION_DENIED} must be enough
+ * for the {@link ContextHubClient} to send at least one message to the nanoapp.
+ */
+ public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1;
+
+ /**
+ * The {@link ContextHubClient} is authorized to communicate with the nanoapp.
+ */
+ public static final int AUTHORIZATION_GRANTED = 2;
+
+ /**
* Constants describing the type of events from a Context Hub.
* {@hide}
*/
@@ -98,6 +140,7 @@ public final class ContextHubManager {
EVENT_NANOAPP_ABORTED,
EVENT_NANOAPP_MESSAGE,
EVENT_HUB_RESET,
+ EVENT_CLIENT_AUTHORIZATION,
})
public @interface Event { }
@@ -138,6 +181,14 @@ public final class ContextHubManager {
*/
public static final int EVENT_HUB_RESET = 6;
+ /**
+ * An event describing a client authorization state change. See
+ * {@link ContextHubClientCallback#onClientAuthorizationChanged} for more details on when this
+ * event will be sent. Contains the EXTRA_NANOAPP_ID and EXTRA_CLIENT_AUTHORIZATION_STATE
+ * extras.
+ */
+ public static final int EVENT_CLIENT_AUTHORIZATION = 7;
+
private final Looper mMainLooper;
private final IContextHubService mService;
private Callback mCallback;
@@ -747,6 +798,14 @@ public final class ContextHubManager {
public void onNanoAppDisabled(long nanoAppId) {
executor.execute(() -> callback.onNanoAppDisabled(client, nanoAppId));
}
+
+ @Override
+ public void onClientAuthorizationChanged(
+ long nanoAppId, @ContextHubManager.AuthorizationState int authorization) {
+ executor.execute(
+ () -> callback.onClientAuthorizationChanged(
+ client, nanoAppId, authorization));
+ }
};
}
@@ -757,9 +816,10 @@ public final class ContextHubManager {
* registration succeeds, the client can send messages to nanoapps through the returned
* {@link ContextHubClient} object, and receive notifications through the provided callback.
*
+ * @param context the context of the application
* @param hubInfo the hub to attach this client to
- * @param callback the notification callback to register
* @param executor the executor to invoke the callback
+ * @param callback the notification callback to register
* @return the registered client object
*
* @throws IllegalArgumentException if hubInfo does not represent a valid hub
@@ -773,8 +833,9 @@ public final class ContextHubManager {
android.Manifest.permission.ACCESS_CONTEXT_HUB
})
@NonNull public ContextHubClient createClient(
- @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback,
- @NonNull @CallbackExecutor Executor executor) {
+ @Nullable Context context, @NonNull ContextHubInfo hubInfo,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull ContextHubClientCallback callback) {
Objects.requireNonNull(callback, "Callback cannot be null");
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
Objects.requireNonNull(executor, "Executor cannot be null");
@@ -783,9 +844,14 @@ public final class ContextHubManager {
IContextHubClientCallback clientInterface = createClientCallback(
client, callback, executor);
+ String attributionTag = null;
+ if (context != null) {
+ attributionTag = context.getAttributionTag();
+ }
+
IContextHubClient clientProxy;
try {
- clientProxy = mService.createClient(hubInfo.getId(), clientInterface);
+ clientProxy = mService.createClient(hubInfo.getId(), clientInterface, attributionTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -794,19 +860,25 @@ public final class ContextHubManager {
return client;
}
+
+ /**
+ * Equivalent to
+ * {@link #createClient(ContextHubInfo, Executor, String, ContextHubClientCallback)}
+ * with the {@link Context} being set to null.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
+ @NonNull public ContextHubClient createClient(
+ @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback,
+ @NonNull @CallbackExecutor Executor executor) {
+ return createClient(null /* context */, hubInfo, executor, callback);
+ }
+
/**
* Equivalent to {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
* with the executor using the main thread's Looper.
- *
- * @param hubInfo the hub to attach this client to
- * @param callback the notification callback to register
- * @return the registered client object
- *
- * @throws IllegalArgumentException if hubInfo does not represent a valid hub
- * @throws IllegalStateException if there were too many registered clients at the service
- * @throws NullPointerException if callback or hubInfo is null
- *
- * @see ContextHubClientCallback
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.LOCATION_HARDWARE,
@@ -814,7 +886,8 @@ public final class ContextHubManager {
})
@NonNull public ContextHubClient createClient(
@NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback) {
- return createClient(hubInfo, callback, new HandlerExecutor(Handler.getMain()));
+ return createClient(null /* context */, hubInfo, new HandlerExecutor(Handler.getMain()),
+ callback);
}
/**
@@ -848,6 +921,8 @@ public final class ContextHubManager {
* on the provided PendingIntent, then the client will be automatically unregistered by the
* service.
*
+ * @param context the context of the application. If a PendingIntent client is recreated,
+ * the latest state in the context will be used and old state will be discarded
* @param hubInfo the hub to attach this client to
* @param pendingIntent the PendingIntent to register to the client
* @param nanoAppId the ID of the nanoapp that Intent events will be generated for
@@ -862,16 +937,22 @@ public final class ContextHubManager {
android.Manifest.permission.ACCESS_CONTEXT_HUB
})
@NonNull public ContextHubClient createClient(
- @NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) {
+ @Nullable Context context, @NonNull ContextHubInfo hubInfo,
+ @NonNull PendingIntent pendingIntent, long nanoAppId) {
Objects.requireNonNull(pendingIntent);
Objects.requireNonNull(hubInfo);
ContextHubClient client = new ContextHubClient(hubInfo, true /* persistent */);
+ String attributionTag = null;
+ if (context != null) {
+ attributionTag = context.getAttributionTag();
+ }
+
IContextHubClient clientProxy;
try {
clientProxy = mService.createPendingIntentClient(
- hubInfo.getId(), pendingIntent, nanoAppId);
+ hubInfo.getId(), pendingIntent, nanoAppId, attributionTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -881,6 +962,19 @@ public final class ContextHubManager {
}
/**
+ * Equivalent to {@link #createClient(ContextHubInfo, PendingIntent, long, String)}
+ * with {@link Context} being set to null.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.LOCATION_HARDWARE,
+ android.Manifest.permission.ACCESS_CONTEXT_HUB
+ })
+ @NonNull public ContextHubClient createClient(
+ @NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) {
+ return createClient(null /* context */, hubInfo, pendingIntent, nanoAppId);
+ }
+
+ /**
* Unregister a callback for receive messages from the context hub.
*
* @see Callback
diff --git a/core/java/android/hardware/location/IContextHubClientCallback.aidl b/core/java/android/hardware/location/IContextHubClientCallback.aidl
index 1c76bcbe18ce..bcd6b087a276 100644
--- a/core/java/android/hardware/location/IContextHubClientCallback.aidl
+++ b/core/java/android/hardware/location/IContextHubClientCallback.aidl
@@ -46,4 +46,7 @@ oneway interface IContextHubClientCallback {
// Callback invoked when a nanoapp is disabled at the attached Context Hub.
void onNanoAppDisabled(long nanoAppId);
+
+ // Callback invoked when the authorization state of a client changes.
+ void onClientAuthorizationChanged(long nanoAppId, int authorization);
}
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 04cc5634bf2c..4961195a3017 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -59,11 +59,13 @@ interface IContextHubService {
int sendMessage(int contextHubHandle, int nanoAppHandle, in ContextHubMessage msg);
// Creates a client to send and receive messages
- IContextHubClient createClient(int contextHubId, in IContextHubClientCallback client);
+ IContextHubClient createClient(
+ int contextHubId, in IContextHubClientCallback client, in String attributionTag);
// Creates a PendingIntent-based client to send and receive messages
IContextHubClient createPendingIntentClient(
- int contextHubId, in PendingIntent pendingIntent, long nanoAppId);
+ int contextHubId, in PendingIntent pendingIntent, long nanoAppId,
+ in String attributionTag);
// Returns a list of ContextHub objects of available hubs
List<ContextHubInfo> getContextHubs();
diff --git a/core/java/android/hardware/location/NanoAppState.java b/core/java/android/hardware/location/NanoAppState.java
index 8de7ecd6b2b3..96b1f19a9cd5 100644
--- a/core/java/android/hardware/location/NanoAppState.java
+++ b/core/java/android/hardware/location/NanoAppState.java
@@ -15,10 +15,14 @@
*/
package android.hardware.location;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A class describing the nanoapp state information resulting from a query to a Context Hub.
*
@@ -29,11 +33,21 @@ public final class NanoAppState implements Parcelable {
private long mNanoAppId;
private int mNanoAppVersion;
private boolean mIsEnabled;
+ private List<String> mNanoAppPermissions;
public NanoAppState(long nanoAppId, int appVersion, boolean enabled) {
mNanoAppId = nanoAppId;
mNanoAppVersion = appVersion;
mIsEnabled = enabled;
+ mNanoAppPermissions = new ArrayList<String>();
+ }
+
+ public NanoAppState(long nanoAppId, int appVersion, boolean enabled,
+ @NonNull List<String> nanoAppPermissions) {
+ mNanoAppId = nanoAppId;
+ mNanoAppVersion = appVersion;
+ mIsEnabled = enabled;
+ mNanoAppPermissions = nanoAppPermissions;
}
/**
@@ -57,10 +71,19 @@ public final class NanoAppState implements Parcelable {
return mIsEnabled;
}
+ /**
+ * @return List of Android permissions that are required to communicate with this app.
+ */
+ public @NonNull List<String> getNanoAppPermissions() {
+ return mNanoAppPermissions;
+ }
+
private NanoAppState(Parcel in) {
mNanoAppId = in.readLong();
mNanoAppVersion = in.readInt();
mIsEnabled = (in.readInt() == 1);
+ mNanoAppPermissions = new ArrayList<String>();
+ in.readStringList(mNanoAppPermissions);
}
@Override
@@ -73,6 +96,7 @@ public final class NanoAppState implements Parcelable {
out.writeLong(mNanoAppId);
out.writeInt(mNanoAppVersion);
out.writeInt(mIsEnabled ? 1 : 0);
+ out.writeStringList(mNanoAppPermissions);
}
public static final @android.annotation.NonNull Creator<NanoAppState> CREATOR =
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 60923f5ea8c6..70bca3019818 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -707,7 +707,7 @@ public final class IpSecManager {
}
/**
- * This class represents an IpSecTunnelInterface.
+ * This class represents an IpSecTunnelInterface
*
* <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
* local endpoints for IPsec tunnels.
@@ -716,7 +716,9 @@ public final class IpSecManager {
* applied to provide IPsec security to packets sent through the tunnel. While a tunnel
* cannot be used in standalone mode within Android, the higher layers may use the tunnel
* to create Network objects which are accessible to the Android system.
+ * @hide
*/
+ @SystemApi
public static final class IpSecTunnelInterface implements AutoCloseable {
private final String mOpPackageName;
private final IIpSecService mService;
@@ -727,26 +729,23 @@ public final class IpSecManager {
private String mInterfaceName;
private int mResourceId = INVALID_RESOURCE_ID;
- /**
- * Get the underlying SPI held by this object.
- *
- * @hide
- */
- @SystemApi
+ /** Get the underlying SPI held by this object. */
@NonNull
public String getInterfaceName() {
return mInterfaceName;
}
/**
- * Add an address to the IpSecTunnelInterface.
+ * Add an address to the IpSecTunnelInterface
*
* <p>Add an address which may be used as the local inner address for
* tunneled traffic.
*
* @param address the local address for traffic inside the tunnel
* @param prefixLen length of the InetAddress prefix
+ * @hide
*/
+ @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -761,13 +760,15 @@ public final class IpSecManager {
}
/**
- * Remove an address from the IpSecTunnelInterface.
+ * Remove an address from the IpSecTunnelInterface
*
- * <p>Remove an address which was previously added to the IpSecTunnelInterface.
+ * <p>Remove an address which was previously added to the IpSecTunnelInterface
*
* @param address to be removed
* @param prefixLen length of the InetAddress prefix
+ * @hide
*/
+ @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -818,7 +819,7 @@ public final class IpSecManager {
}
/**
- * Delete an IpSecTunnelInterface.
+ * Delete an IpSecTunnelInterface
*
* <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
* resources. Any packets bound for this interface either inbound or outbound will
@@ -840,12 +841,7 @@ public final class IpSecManager {
}
}
-
- /**
- * Check that the Interface was closed properly.
- *
- * @hide
- */
+ /** Check that the Interface was closed properly. */
@Override
protected void finalize() throws Throwable {
if (mCloseGuard != null) {
@@ -877,52 +873,17 @@ public final class IpSecManager {
* Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
*
* <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
- * underlying network disconnects, and the {@link
- * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
- *
- * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
- * that go through the tunnel will need a underlying network to transit to the IPsec peer.
- * This network should almost certainly be a physical network such as WiFi.
- * @return a new {@link IpSecTunnelInterface} with the specified properties
- * @throws IOException indicating that the tunnel could not be created due to a lower-layer
- * error
- * @throws ResourceUnavailableException indicating that the number of opening tunnels has
- * reached the limit.
- */
- @NonNull
- @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
- @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
- public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull Network underlyingNetwork)
- throws ResourceUnavailableException, IOException {
-
- // TODO: Remove the need for adding two unused addresses with IPsec tunnels when {@link
- // #createIpSecTunnelInterface(localAddress, remoteAddress, underlyingNetwork)} can be
- // safely removed.
- final InetAddress address = InetAddress.getLocalHost();
- return createIpSecTunnelInterface(address, address, underlyingNetwork);
- }
-
- /**
- * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
+ * underlying network goes away, and the onLost() callback is received.
*
- * <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
- * underlying network disconnects, and the {@link
- * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
- *
- * @param localAddress The local address of the tunnel
- * @param remoteAddress The local address of the tunnel
- * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
- * that go through the tunnel will need a underlying network to transit to the IPsec peer.
- * This network should almost certainly be a physical network such as WiFi.
- * @return a new {@link IpSecTunnelInterface} with the specified properties
- * @throws IOException indicating that the tunnel could not be created due to a lower-layer
- * error
- * @throws ResourceUnavailableException indicating that the number of opening tunnels has
- * reached the limit.
+ * @param localAddress The local addres of the tunnel
+ * @param remoteAddress The local addres of the tunnel
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
+ * This network should almost certainly be a network such as WiFi with an L2 address.
+ * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the socket could not be opened or bound
+ * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
* @hide
- * @deprecated Callers should use {@link #createIpSecTunnelInterface(Network)}
*/
- @Deprecated
@SystemApi
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@@ -946,14 +907,16 @@ public final class IpSecManager {
* <p>Applications should probably not use this API directly.
*
*
- * @param tunnel The {@link IpSecTunnelInterface} that will use the supplied
+ * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
* transform.
- * @param direction the direction, {@link #DIRECTION_OUT} or {@link #DIRECTION_IN} in which
+ * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
* the transform will be used.
* @param transform an {@link IpSecTransform} created in tunnel mode
- * @throws IOException indicating that the transform could not be applied due to a lower-layer
- * error
+ * @throws IOException indicating that the transform could not be applied due to a lower
+ * layer failure.
+ * @hide
*/
+ @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index 250cff29944a..a22d41a5ef86 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -26,7 +26,7 @@ import android.view.View;
import android.widget.TextView;
import com.android.internal.util.HexDump;
-import com.android.org.bouncycastle.asn1.x509.X509Name;
+import com.android.internal.org.bouncycastle.asn1.x509.X509Name;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java
index 869a72717f9f..cb4e9cba0977 100644
--- a/core/java/android/os/CombinedVibrationEffect.java
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -364,8 +364,22 @@ public abstract class CombinedVibrationEffect implements Parcelable {
@Override
public long getDuration() {
long maxDuration = Long.MIN_VALUE;
+ boolean hasUnknownStep = false;
for (int i = 0; i < mEffects.size(); i++) {
- maxDuration = Math.max(maxDuration, mEffects.valueAt(i).getDuration());
+ long duration = mEffects.valueAt(i).getDuration();
+ if (duration == Long.MAX_VALUE) {
+ // If any duration is repeating, this combination duration is also repeating.
+ return duration;
+ }
+ maxDuration = Math.max(maxDuration, duration);
+ // If any step is unknown, this combination duration will also be unknown, unless
+ // any step is repeating. Repeating vibrations take precedence over non-repeating
+ // ones in the service, so continue looping to check for repeating steps.
+ hasUnknownStep |= duration < 0;
+ }
+ if (hasUnknownStep) {
+ // If any step is unknown, this combination duration is also unknown.
+ return -1;
}
return maxDuration;
}
@@ -477,16 +491,25 @@ public abstract class CombinedVibrationEffect implements Parcelable {
@Override
public long getDuration() {
+ boolean hasUnknownStep = false;
long durations = 0;
final int effectCount = mEffects.size();
for (int i = 0; i < effectCount; i++) {
CombinedVibrationEffect effect = mEffects.get(i);
long duration = effect.getDuration();
- if (duration < 0) {
- // If any duration is unknown, this combination duration is also unknown.
+ if (duration == Long.MAX_VALUE) {
+ // If any duration is repeating, this combination duration is also repeating.
return duration;
}
durations += duration;
+ // If any step is unknown, this combination duration will also be unknown, unless
+ // any step is repeating. Repeating vibrations take precedence over non-repeating
+ // ones in the service, so continue looping to check for repeating steps.
+ hasUnknownStep |= duration < 0;
+ }
+ if (hasUnknownStep) {
+ // If any step is unknown, this combination duration is also unknown.
+ return -1;
}
long delays = 0;
for (int i = 0; i < effectCount; i++) {
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index 804dc102c3f6..f9e294791cca 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.CombinedVibrationEffect;
+import android.os.IVibratorStateListener;
import android.os.VibrationAttributes;
import android.os.VibratorInfo;
@@ -24,6 +25,9 @@ import android.os.VibratorInfo;
interface IVibratorManagerService {
int[] getVibratorIds();
VibratorInfo getVibratorInfo(int vibratorId);
+ boolean isVibrating(int vibratorId);
+ boolean registerVibratorStateListener(int vibratorId, in IVibratorStateListener listener);
+ boolean unregisterVibratorStateListener(int vibratorId, in IVibratorStateListener listener);
boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
in CombinedVibrationEffect effect, in VibrationAttributes attributes);
void vibrate(int uid, String opPkg, in CombinedVibrationEffect effect,
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 9a102a7c4b00..059e932a9da9 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -416,9 +416,21 @@ public final class PowerManager {
public static final int GO_TO_SLEEP_REASON_QUIESCENT = 10;
/**
+ * Go to sleep reason code: The last powered on display group has been removed.
* @hide
*/
- public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_QUIESCENT;
+ public static final int GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED = 11;
+
+ /**
+ * Go to sleep reason code: Every display group has been turned off.
+ * @hide
+ */
+ public static final int GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF = 12;
+
+ /**
+ * @hide
+ */
+ public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF;
/**
* @hide
@@ -435,6 +447,8 @@ public final class PowerManager {
case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility";
case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend";
case GO_TO_SLEEP_REASON_INATTENTIVE: return "inattentive";
+ case GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED: return "display_group_removed";
+ case GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF: return "display_groups_turned_off";
default: return Integer.toString(sleepReason);
}
}
@@ -521,6 +535,8 @@ public final class PowerManager {
WAKE_REASON_WAKE_KEY,
WAKE_REASON_WAKE_MOTION,
WAKE_REASON_HDMI,
+ WAKE_REASON_DISPLAY_GROUP_ADDED,
+ WAKE_REASON_DISPLAY_GROUP_TURNED_ON,
})
@Retention(RetentionPolicy.SOURCE)
public @interface WakeReason{}
@@ -608,6 +624,18 @@ public final class PowerManager {
public static final int WAKE_REASON_LID = 9;
/**
+ * Wake up reason code: Waking due to display group being added.
+ * @hide
+ */
+ public static final int WAKE_REASON_DISPLAY_GROUP_ADDED = 10;
+
+ /**
+ * Wake up reason code: Waking due to display group being powered on.
+ * @hide
+ */
+ public static final int WAKE_REASON_DISPLAY_GROUP_TURNED_ON = 11;
+
+ /**
* Convert the wake reason to a string for debugging purposes.
* @hide
*/
@@ -623,6 +651,8 @@ public final class PowerManager {
case WAKE_REASON_WAKE_MOTION: return "WAKE_REASON_WAKE_MOTION";
case WAKE_REASON_HDMI: return "WAKE_REASON_HDMI";
case WAKE_REASON_LID: return "WAKE_REASON_LID";
+ case WAKE_REASON_DISPLAY_GROUP_ADDED: return "WAKE_REASON_DISPLAY_GROUP_ADDED";
+ case WAKE_REASON_DISPLAY_GROUP_TURNED_ON: return "WAKE_REASON_DISPLAY_GROUP_TURNED_ON";
default: return Integer.toString(wakeReason);
}
}
@@ -1253,8 +1283,15 @@ public final class PowerManager {
}
}
- /**
- * Forces the device to go to sleep.
+ /**
+ * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+ * to turn off.
+ *
+ * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+ * turned on it will be turned off. If all displays are off as a result of this action the
+ * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT
+ * default display group} is already off then nothing will happen.
+ *
* <p>
* Overrides all the wake locks that are held.
* This is what happens when the power key is pressed to turn off the screen.
@@ -1277,7 +1314,14 @@ public final class PowerManager {
}
/**
- * Forces the device to go to sleep.
+ * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+ * to turn off.
+ *
+ * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+ * turned on it will be turned off. If all displays are off as a result of this action the
+ * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT
+ * default display group} is already off then nothing will happen.
+ *
* <p>
* Overrides all the wake locks that are held.
* This is what happens when the power key is pressed to turn off the screen.
@@ -1307,9 +1351,15 @@ public final class PowerManager {
}
/**
- * Forces the device to wake up from sleep.
+ * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+ * to turn on.
+ *
+ * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+ * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
+ * the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is already
+ * on then nothing will happen.
+ *
* <p>
- * If the device is currently asleep, wakes it up, otherwise does nothing.
* This is what happens when the power key is pressed to turn on the screen.
* </p><p>
* Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
@@ -1332,9 +1382,15 @@ public final class PowerManager {
}
/**
- * Forces the device to wake up from sleep.
+ * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+ * to turn on.
+ *
+ * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+ * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
+ * the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is already
+ * on then nothing will happen.
+ *
* <p>
- * If the device is currently asleep, wakes it up, otherwise does nothing.
* This is what happens when the power key is pressed to turn on the screen.
* </p><p>
* Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
@@ -1361,9 +1417,13 @@ public final class PowerManager {
}
/**
- * Forces the device to wake up from sleep.
+ * Forces the {@link android.view.Display#DEFAULT_DISPLAY default display} to turn on.
+ *
+ * <p>If the {@link android.view.Display#DEFAULT_DISPLAY default display} is turned off it will
+ * be turned on. Additionally, if the device is asleep it will be awoken. If the {@link
+ * android.view.Display#DEFAULT_DISPLAY default display} is already on then nothing will happen.
+ *
* <p>
- * If the device is currently asleep, wakes it up, otherwise does nothing.
* This is what happens when the power key is pressed to turn on the screen.
* </p><p>
* Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
diff --git a/core/java/android/os/strictmode/IncorrectContextUseViolation.java b/core/java/android/os/strictmode/IncorrectContextUseViolation.java
index 647db171e080..11d26cab14b3 100644
--- a/core/java/android/os/strictmode/IncorrectContextUseViolation.java
+++ b/core/java/android/os/strictmode/IncorrectContextUseViolation.java
@@ -16,19 +16,20 @@
package android.os.strictmode;
+import android.annotation.NonNull;
import android.content.Context;
/**
- * Incorrect usage of {@link Context}, such as obtaining a visual service from non-visual
- * {@link Context} instance.
+ * Incorrect usage of {@link Context}, such as obtaining a UI service from non-UI {@link Context}
+ * instance.
+ *
* @see Context#getSystemService(String)
- * @see Context#getDisplayNoVerify()
- * @hide
+ * @see Context#isUiContext(Context)
+ * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
*/
public final class IncorrectContextUseViolation extends Violation {
- /** @hide */
- public IncorrectContextUseViolation(String message, Throwable originStack) {
+ public IncorrectContextUseViolation(@NonNull String message, @NonNull Throwable originStack) {
super(message);
initCause(originStack);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6dab4c04d000..f6ef8a3d8dae 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13393,6 +13393,24 @@ public final class Settings {
"euicc_factory_reset_timeout_millis";
/**
+ * Flag to set the waiting time for euicc slot switch.
+ * Type: long
+ *
+ * @hide
+ */
+ public static final String EUICC_SWITCH_SLOT_TIMEOUT_MILLIS =
+ "euicc_switch_slot_timeout_millis";
+
+ /**
+ * Flag to set the waiting time for enabling multi SIM slot.
+ * Type: long
+ *
+ * @hide
+ */
+ public static final String ENABLE_MULTI_SLOT_TIMEOUT_MILLIS =
+ "enable_multi_slot_timeout_millis";
+
+ /**
* Flag to set the timeout for when to refresh the storage settings cached data.
* Type: long
*
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 4679c56de612..e3d0741b4603 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -129,14 +129,14 @@ public final class Dataset implements Parcelable {
/** @hide */
@TestApi
- @SuppressLint("ConcreteCollection")
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
public @Nullable ArrayList<AutofillId> getFieldIds() {
return mFieldIds;
}
/** @hide */
@TestApi
- @SuppressLint("ConcreteCollection")
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
public @Nullable ArrayList<AutofillValue> getFieldValues() {
return mFieldValues;
}
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index e0f3018e3d0e..44daeff76997 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -46,7 +46,7 @@ oneway interface INotificationListener
void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType);
// assistants only
- void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel);
+ void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel, in NotificationRankingUpdate update);
void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
void onNotificationsSeen(in List<String> keys);
void onPanelRevealed(int items);
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index cf2152cc3ad4..1d49a7206023 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -126,7 +126,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
* {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p>
*
* @param sbn the new notification
- * @return an adjustment or null to take no action, within 100ms.
+ * @return an adjustment or null to take no action, within 200ms.
*/
abstract public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn);
@@ -135,7 +135,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
*
* @param sbn the new notification
* @param channel the channel the notification was posted to
- * @return an adjustment or null to take no action, within 100ms.
+ * @return an adjustment or null to take no action, within 200ms.
*/
public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn,
@NonNull NotificationChannel channel) {
@@ -143,6 +143,20 @@ public abstract class NotificationAssistantService extends NotificationListenerS
}
/**
+ * A notification was posted by an app. Called before post.
+ *
+ * @param sbn the new notification
+ * @param channel the channel the notification was posted to
+ * @param rankingMap The current ranking map that can be used to retrieve ranking information
+ * for active notifications.
+ * @return an adjustment or null to take no action, within 200ms.
+ */
+ public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn,
+ @NonNull NotificationChannel channel, @NonNull RankingMap rankingMap) {
+ return onNotificationEnqueued(sbn, channel);
+ }
+
+ /**
* Implement this method to learn when notifications are removed, how they were interacted with
* before removal, and why they were removed.
* <p>
@@ -316,7 +330,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper {
@Override
public void onNotificationEnqueuedWithChannel(IStatusBarNotificationHolder sbnHolder,
- NotificationChannel channel) {
+ NotificationChannel channel, NotificationRankingUpdate update) {
StatusBarNotification sbn;
try {
sbn = sbnHolder.get();
@@ -330,9 +344,11 @@ public abstract class NotificationAssistantService extends NotificationListenerS
return;
}
+ applyUpdateLocked(update);
SomeArgs args = SomeArgs.obtain();
args.arg1 = sbn;
args.arg2 = channel;
+ args.arg3 = getCurrentRanking();
mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED,
args).sendToTarget();
}
@@ -472,8 +488,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS
SomeArgs args = (SomeArgs) msg.obj;
StatusBarNotification sbn = (StatusBarNotification) args.arg1;
NotificationChannel channel = (NotificationChannel) args.arg2;
+ RankingMap ranking = (RankingMap) args.arg3;
args.recycle();
- Adjustment adjustment = onNotificationEnqueued(sbn, channel);
+ Adjustment adjustment = onNotificationEnqueued(sbn, channel, ranking);
setAdjustmentIssuer(adjustment);
if (adjustment != null) {
if (!isBound()) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index c41e599d5cae..64cddc35d2bb 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1431,7 +1431,8 @@ public abstract class NotificationListenerService extends Service {
@Override
public void onNotificationEnqueuedWithChannel(
- IStatusBarNotificationHolder notificationHolder, NotificationChannel channel)
+ IStatusBarNotificationHolder notificationHolder, NotificationChannel channel,
+ NotificationRankingUpdate update)
throws RemoteException {
// no-op in the listener
}
diff --git a/core/java/android/service/smartspace/ISmartspaceService.aidl b/core/java/android/service/smartspace/ISmartspaceService.aidl
new file mode 100644
index 000000000000..c9c6807058af
--- /dev/null
+++ b/core/java/android/service/smartspace/ISmartspaceService.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.smartspace;
+
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.ISmartspaceCallback;
+import android.content.pm.ParceledListSlice;
+
+/**
+ * Interface from the system to Smartspace service.
+ *
+ * @hide
+ */
+oneway interface ISmartspaceService {
+
+ void onCreateSmartspaceSession(in SmartspaceConfig context, in SmartspaceSessionId sessionId);
+
+ void notifySmartspaceEvent(in SmartspaceSessionId sessionId, in SmartspaceTargetEvent event);
+
+ void requestSmartspaceUpdate(in SmartspaceSessionId sessionId);
+
+ void registerSmartspaceUpdates(in SmartspaceSessionId sessionId,
+ in ISmartspaceCallback callback);
+
+ void unregisterSmartspaceUpdates(in SmartspaceSessionId sessionId,
+ in ISmartspaceCallback callback);
+
+ void onDestroySmartspaceSession(in SmartspaceSessionId sessionId);
+}
diff --git a/core/java/android/service/smartspace/SmartspaceService.java b/core/java/android/service/smartspace/SmartspaceService.java
new file mode 100644
index 000000000000..09b731091bfd
--- /dev/null
+++ b/core/java/android/service/smartspace/SmartspaceService.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.smartspace;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.smartspace.ISmartspaceCallback;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.Intent;
+import android.content.pm.ParceledListSlice;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.service.smartspace.ISmartspaceService.Stub;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A service used to share the lifecycle of smartspace UI (open, close, interaction)
+ * and also to return smartspace result on a query.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class SmartspaceService extends Service {
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ *
+ * <p>The service must also require the {@link android.permission#MANAGE_SMARTSPACE}
+ * permission.
+ *
+ * @hide
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.smartspace.SmartspaceService";
+ private static final boolean DEBUG = false;
+ private static final String TAG = "SmartspaceService";
+ private final ArrayMap<SmartspaceSessionId, ArrayList<CallbackWrapper>> mSessionCallbacks =
+ new ArrayMap<>();
+ private Handler mHandler;
+
+ private final android.service.smartspace.ISmartspaceService mInterface = new Stub() {
+
+ @Override
+ public void onCreateSmartspaceSession(SmartspaceConfig smartspaceConfig,
+ SmartspaceSessionId sessionId) {
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::doCreateSmartspaceSession,
+ SmartspaceService.this, smartspaceConfig, sessionId));
+ }
+
+ @Override
+ public void notifySmartspaceEvent(SmartspaceSessionId sessionId,
+ SmartspaceTargetEvent event) {
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::notifySmartspaceEvent,
+ SmartspaceService.this, sessionId, event));
+ }
+
+ @Override
+ public void requestSmartspaceUpdate(SmartspaceSessionId sessionId) {
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::doRequestPredictionUpdate,
+ SmartspaceService.this, sessionId));
+ }
+
+ @Override
+ public void registerSmartspaceUpdates(SmartspaceSessionId sessionId,
+ ISmartspaceCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::doRegisterSmartspaceUpdates,
+ SmartspaceService.this, sessionId, callback));
+ }
+
+ @Override
+ public void unregisterSmartspaceUpdates(SmartspaceSessionId sessionId,
+ ISmartspaceCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::doUnregisterSmartspaceUpdates,
+ SmartspaceService.this, sessionId, callback));
+ }
+
+ @Override
+ public void onDestroySmartspaceSession(SmartspaceSessionId sessionId) {
+
+ mHandler.sendMessage(
+ obtainMessage(SmartspaceService::doDestroy,
+ SmartspaceService.this, sessionId));
+ }
+ };
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.d(TAG, "onCreate mSessionCallbacks: " + mSessionCallbacks);
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @Override
+ @NonNull
+ public final IBinder onBind(@NonNull Intent intent) {
+ Log.d(TAG, "onBind mSessionCallbacks: " + mSessionCallbacks);
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Slog.w(TAG, "Tried to bind to wrong intent (should be "
+ + SERVICE_INTERFACE + ": " + intent);
+ return null;
+ }
+
+ private void doCreateSmartspaceSession(@NonNull SmartspaceConfig config,
+ @NonNull SmartspaceSessionId sessionId) {
+ Log.d(TAG, "doCreateSmartspaceSession mSessionCallbacks: " + mSessionCallbacks);
+ mSessionCallbacks.put(sessionId, new ArrayList<>());
+ onCreateSmartspaceSession(config, sessionId);
+ }
+
+ /**
+ * Gets called when the client calls <code> SmartspaceManager#createSmartspaceSession </code>.
+ */
+ public abstract void onCreateSmartspaceSession(@NonNull SmartspaceConfig config,
+ @NonNull SmartspaceSessionId sessionId);
+
+ /**
+ * Gets called when the client calls <code> SmartspaceSession#notifySmartspaceEvent </code>.
+ */
+ @MainThread
+ public abstract void notifySmartspaceEvent(@NonNull SmartspaceSessionId sessionId,
+ @NonNull SmartspaceTargetEvent event);
+
+ /**
+ * Gets called when the client calls <code> SmartspaceSession#requestSmartspaceUpdate </code>.
+ */
+ @MainThread
+ public abstract void onRequestSmartspaceUpdate(@NonNull SmartspaceSessionId sessionId);
+
+ private void doRegisterSmartspaceUpdates(@NonNull SmartspaceSessionId sessionId,
+ @NonNull ISmartspaceCallback callback) {
+ Log.d(TAG, "doRegisterSmartspaceUpdates mSessionCallbacks: " + mSessionCallbacks);
+ final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+ if (callbacks == null) {
+ Slog.e(TAG, "Failed to register for updates for unknown session: " + sessionId);
+ return;
+ }
+
+ final CallbackWrapper wrapper = findCallbackWrapper(callbacks, callback);
+ if (wrapper == null) {
+ callbacks.add(new CallbackWrapper(callback,
+ callbackWrapper ->
+ mHandler.post(
+ () -> removeCallbackWrapper(callbacks, callbackWrapper))));
+ }
+ }
+
+ private void doUnregisterSmartspaceUpdates(@NonNull SmartspaceSessionId sessionId,
+ @NonNull ISmartspaceCallback callback) {
+ Log.d(TAG, "doUnregisterSmartspaceUpdates mSessionCallbacks: " + mSessionCallbacks);
+ final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+ if (callbacks == null) {
+ Slog.e(TAG, "Failed to unregister for updates for unknown session: " + sessionId);
+ return;
+ }
+
+ final CallbackWrapper wrapper = findCallbackWrapper(callbacks, callback);
+ if (wrapper != null) {
+ removeCallbackWrapper(callbacks, wrapper);
+ }
+ }
+
+ private void doRequestPredictionUpdate(@NonNull SmartspaceSessionId sessionId) {
+ Log.d(TAG, "doRequestPredictionUpdate mSessionCallbacks: " + mSessionCallbacks);
+ // Just an optimization, if there are no callbacks, then don't bother notifying the service
+ final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+ if (callbacks != null && !callbacks.isEmpty()) {
+ onRequestSmartspaceUpdate(sessionId);
+ }
+ }
+
+ /**
+ * Finds the callback wrapper for the given callback.
+ */
+ private CallbackWrapper findCallbackWrapper(ArrayList<CallbackWrapper> callbacks,
+ ISmartspaceCallback callback) {
+ for (int i = callbacks.size() - 1; i >= 0; i--) {
+ if (callbacks.get(i).isCallback(callback)) {
+ return callbacks.get(i);
+ }
+ }
+ return null;
+ }
+
+ private void removeCallbackWrapper(
+ ArrayList<CallbackWrapper> callbacks, CallbackWrapper wrapper) {
+ if (callbacks == null) {
+ return;
+ }
+ callbacks.remove(wrapper);
+ }
+
+ /**
+ * Gets called when the client calls <code> SmartspaceManager#destroy() </code>.
+ */
+ public abstract void onDestroySmartspaceSession(@NonNull SmartspaceSessionId sessionId);
+
+ private void doDestroy(@NonNull SmartspaceSessionId sessionId) {
+ Log.d(TAG, "doDestroy mSessionCallbacks: " + mSessionCallbacks);
+ super.onDestroy();
+ mSessionCallbacks.remove(sessionId);
+ onDestroySmartspaceSession(sessionId);
+ }
+
+ /**
+ * Used by the prediction factory to send back results the client app. The can be called
+ * in response to {@link #onRequestSmartspaceUpdate(SmartspaceSessionId)} or proactively as
+ * a result of changes in predictions.
+ */
+ public final void updateSmartspaceTargets(@NonNull SmartspaceSessionId sessionId,
+ @NonNull List<SmartspaceTarget> targets) {
+ Log.d(TAG, "updateSmartspaceTargets mSessionCallbacks: " + mSessionCallbacks);
+ List<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+ if (callbacks != null) {
+ for (CallbackWrapper callback : callbacks) {
+ callback.accept(targets);
+ }
+ }
+ }
+
+ /**
+ * Destroys a smartspace session.
+ */
+ @MainThread
+ public abstract void onDestroy(@NonNull SmartspaceSessionId sessionId);
+
+ private static final class CallbackWrapper implements Consumer<List<SmartspaceTarget>>,
+ IBinder.DeathRecipient {
+
+ private final Consumer<CallbackWrapper> mOnBinderDied;
+ private ISmartspaceCallback mCallback;
+
+ CallbackWrapper(ISmartspaceCallback callback,
+ @Nullable Consumer<CallbackWrapper> onBinderDied) {
+ mCallback = callback;
+ mOnBinderDied = onBinderDied;
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death: " + e);
+ }
+ }
+
+ public boolean isCallback(@NonNull ISmartspaceCallback callback) {
+ if (mCallback == null) {
+ Slog.e(TAG, "Callback is null, likely the binder has died.");
+ return false;
+ }
+ return mCallback.equals(callback);
+ }
+
+ @Override
+ public void accept(List<SmartspaceTarget> smartspaceTargets) {
+ try {
+ if (mCallback != null) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "CallbackWrapper.accept smartspaceTargets=" + smartspaceTargets);
+ }
+ mCallback.onResult(new ParceledListSlice(smartspaceTargets));
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result:" + e);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ mCallback = null;
+ if (mOnBinderDied != null) {
+ mOnBinderDied.accept(this);
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 68d6f3fe3743..25f80900f1cf 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -345,7 +345,8 @@ public class VoiceInteractionService extends Service {
*/
@SystemApi
@HotwordConfigResult
- public final int setHotwordDetectionConfig(@Nullable Bundle options) {
+ public final int setHotwordDetectionConfig(
+ @SuppressLint("NullableCollection") @Nullable Bundle options) {
if (mSystemService == null) {
throw new IllegalStateException("Not available until onReady() is called");
}
diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java
index 723f1dd15e23..49ff237403b2 100644
--- a/core/java/android/util/imetracing/ImeTracing.java
+++ b/core/java/android/util/imetracing/ImeTracing.java
@@ -28,6 +28,8 @@ import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.inputmethod.InputMethodManager;
+import com.android.internal.inputmethod.Completable;
+import com.android.internal.inputmethod.ResultCallbacks;
import com.android.internal.view.IInputMethodManager;
import java.io.PrintWriter;
@@ -91,7 +93,9 @@ public abstract class ImeTracing {
* @param where
*/
public void sendToService(byte[] protoDump, int source, String where) throws RemoteException {
- mService.startProtoDump(protoDump, source, where);
+ final Completable.Void value = Completable.createVoid();
+ mService.startProtoDump(protoDump, source, where, ResultCallbacks.of(value));
+ Completable.getResult(value);
}
/**
diff --git a/core/java/android/util/imetracing/OWNERS b/core/java/android/util/imetracing/OWNERS
new file mode 100644
index 000000000000..885fd0ab9a45
--- /dev/null
+++ b/core/java/android/util/imetracing/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/android/view/ContentInfo.java b/core/java/android/view/ContentInfo.java
index bc66ea16bb72..547bc9d49380 100644
--- a/core/java/android/view/ContentInfo.java
+++ b/core/java/android/view/ContentInfo.java
@@ -19,6 +19,7 @@ package android.view;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -204,6 +205,7 @@ public final class ContentInfo {
* the IME.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public Bundle getExtras() {
return mExtras;
}
@@ -347,7 +349,7 @@ public final class ContentInfo {
* @return this builder
*/
@NonNull
- public Builder setExtras(@Nullable Bundle extras) {
+ public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) {
mExtras = extras;
return this;
}
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index f4b90e1f7b44..bc03222f390d 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -22,6 +22,7 @@ import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.hardware.Battery;
import android.hardware.SensorManager;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
@@ -73,6 +74,7 @@ public final class InputDevice implements Parcelable {
private final boolean mHasMicrophone;
private final boolean mHasButtonUnderPad;
private final boolean mHasSensor;
+ private final boolean mHasBattery;
private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
@GuardedBy("mMotionRanges")
@@ -84,6 +86,9 @@ public final class InputDevice implements Parcelable {
@GuardedBy("mMotionRanges")
private SensorManager mSensorManager;
+ @GuardedBy("mMotionRanges")
+ private Battery mBattery;
+
/**
* A mask for input source classes.
*
@@ -323,6 +328,13 @@ public final class InputDevice implements Parcelable {
public static final int SOURCE_HDMI = 0x02000000 | SOURCE_CLASS_BUTTON;
/**
+ * The input source is a sensor associated with the input device.
+ *
+ * @see #SOURCE_CLASS_NONE
+ */
+ public static final int SOURCE_SENSOR = 0x04000000 | SOURCE_CLASS_NONE;
+
+ /**
* A special input source constant that is used when filtering input devices
* to match devices that provide any type of input source.
*/
@@ -448,7 +460,7 @@ public final class InputDevice implements Parcelable {
public InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
KeyCharacterMap keyCharacterMap, boolean hasVibrator, boolean hasMicrophone,
- boolean hasButtonUnderPad, boolean hasSensor) {
+ boolean hasButtonUnderPad, boolean hasSensor, boolean hasBattery) {
mId = id;
mGeneration = generation;
mControllerNumber = controllerNumber;
@@ -464,6 +476,7 @@ public final class InputDevice implements Parcelable {
mHasMicrophone = hasMicrophone;
mHasButtonUnderPad = hasButtonUnderPad;
mHasSensor = hasSensor;
+ mHasBattery = hasBattery;
mIdentifier = new InputDeviceIdentifier(descriptor, vendorId, productId);
}
@@ -483,6 +496,7 @@ public final class InputDevice implements Parcelable {
mHasMicrophone = in.readInt() != 0;
mHasButtonUnderPad = in.readInt() != 0;
mHasSensor = in.readInt() != 0;
+ mHasBattery = in.readInt() != 0;
mIdentifier = new InputDeviceIdentifier(mDescriptor, mVendorId, mProductId);
int numRanges = in.readInt();
@@ -830,6 +844,22 @@ public final class InputDevice implements Parcelable {
}
/**
+ * Gets the battery object associated with the device, if there is one.
+ * Even if the device does not have a battery, the result is never null.
+ * Use {@link Battery#hasBattery} to determine whether a battery is
+ * present.
+ *
+ * @return The battery object associated with the device, never null.
+ */
+ @NonNull
+ public Battery getBattery() {
+ if (mBattery == null) {
+ mBattery = InputManager.getInstance().getInputDeviceBattery(mId, mHasBattery);
+ }
+ return mBattery;
+ }
+
+ /**
* Gets the sensor manager service associated with the input device.
* Even if the device does not have a sensor, the result is never null.
* Use {@link SensorManager#getSensorList} to get a full list of all supported sensors.
@@ -1051,6 +1081,7 @@ public final class InputDevice implements Parcelable {
out.writeInt(mHasMicrophone ? 1 : 0);
out.writeInt(mHasButtonUnderPad ? 1 : 0);
out.writeInt(mHasSensor ? 1 : 0);
+ out.writeInt(mHasBattery ? 1 : 0);
final int numRanges = mMotionRanges.size();
out.writeInt(numRanges);
@@ -1097,6 +1128,8 @@ public final class InputDevice implements Parcelable {
description.append(" Has Sensor: ").append(mHasSensor).append("\n");
+ description.append(" Has battery: ").append(mHasBattery).append("\n");
+
description.append(" Has mic: ").append(mHasMicrophone).append("\n");
description.append(" Sources: 0x").append(Integer.toHexString(mSources)).append(" (");
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index bf377b0bcfd7..d68e9032c19d 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -106,7 +106,9 @@ public class InsetsState implements Parcelable {
public static final int ITYPE_NAVIGATION_BAR = 1;
public static final int ITYPE_CAPTION_BAR = 2;
- public static final int ITYPE_TOP_GESTURES = 3;
+ // The always visible types are visible to all windows regardless of the z-order.
+ public static final int FIRST_ALWAYS_VISIBLE_TYPE = 3;
+ public static final int ITYPE_TOP_GESTURES = FIRST_ALWAYS_VISIBLE_TYPE;
public static final int ITYPE_BOTTOM_GESTURES = 4;
public static final int ITYPE_LEFT_GESTURES = 5;
public static final int ITYPE_RIGHT_GESTURES = 6;
@@ -117,15 +119,16 @@ public class InsetsState implements Parcelable {
public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10;
- public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 11;
- public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 12;
- public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 13;
- public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 14;
+ public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 11;
+ public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12;
+ public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13;
+ public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14;
+ public static final int LAST_ALWAYS_VISIBLE_TYPE = ITYPE_BOTTOM_DISPLAY_CUTOUT;
- public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 15;
- public static final int ITYPE_TOP_DISPLAY_CUTOUT = 16;
- public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 17;
- public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 18;
+ public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15;
+ public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16;
+ public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17;
+ public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18;
/** Input method window. */
public static final int ITYPE_IME = 19;
@@ -182,6 +185,18 @@ public class InsetsState implements Parcelable {
}
/**
+ * Mirror the always visible sources from the other state. They will share the same object for
+ * the always visible types.
+ *
+ * @param other the state to mirror the mirrored sources from.
+ */
+ public void mirrorAlwaysVisibleInsetsSources(InsetsState other) {
+ for (int type = FIRST_ALWAYS_VISIBLE_TYPE; type <= LAST_ALWAYS_VISIBLE_TYPE; type++) {
+ mSources[type] = other.mSources[type];
+ }
+ }
+
+ /**
* Calculates {@link WindowInsets} based on the current source configuration.
*
* @param frame The frame to calculate the insets relative to.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0a1a23116941..acd25077fb5a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -750,18 +750,22 @@ public final class SurfaceControl implements Parcelable {
private abstract static class CaptureArgs {
private final int mPixelFormat;
private final Rect mSourceCrop = new Rect();
- private final float mFrameScale;
+ private final float mFrameScaleX;
+ private final float mFrameScaleY;
private final boolean mCaptureSecureLayers;
private final boolean mAllowProtected;
private final long mUid;
+ private final boolean mGrayscale;
private CaptureArgs(Builder<? extends Builder<?>> builder) {
mPixelFormat = builder.mPixelFormat;
mSourceCrop.set(builder.mSourceCrop);
- mFrameScale = builder.mFrameScale;
+ mFrameScaleX = builder.mFrameScaleX;
+ mFrameScaleY = builder.mFrameScaleY;
mCaptureSecureLayers = builder.mCaptureSecureLayers;
mAllowProtected = builder.mAllowProtected;
mUid = builder.mUid;
+ mGrayscale = builder.mGrayscale;
}
/**
@@ -772,10 +776,12 @@ public final class SurfaceControl implements Parcelable {
abstract static class Builder<T extends Builder<T>> {
private int mPixelFormat = PixelFormat.RGBA_8888;
private final Rect mSourceCrop = new Rect();
- private float mFrameScale = 1;
+ private float mFrameScaleX = 1;
+ private float mFrameScaleY = 1;
private boolean mCaptureSecureLayers;
private boolean mAllowProtected;
private long mUid = -1;
+ private boolean mGrayscale;
/**
* The desired pixel format of the returned buffer.
@@ -798,7 +804,18 @@ public final class SurfaceControl implements Parcelable {
* The desired scale of the returned buffer. The raw screen will be scaled up/down.
*/
public T setFrameScale(float frameScale) {
- mFrameScale = frameScale;
+ mFrameScaleX = frameScale;
+ mFrameScaleY = frameScale;
+ return getThis();
+ }
+
+ /**
+ * The desired scale of the returned buffer, allowing separate values for x and y scale.
+ * The raw screen will be scaled up/down.
+ */
+ public T setFrameScale(float frameScaleX, float frameScaleY) {
+ mFrameScaleX = frameScaleX;
+ mFrameScaleY = frameScaleY;
return getThis();
}
@@ -834,6 +851,14 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Set whether the screenshot should use grayscale or not.
+ */
+ public T setGrayscale(boolean grayscale) {
+ mGrayscale = grayscale;
+ return getThis();
+ }
+
+ /**
* Each sub class should return itself to allow the builder to chain properly
*/
abstract T getThis();
@@ -929,7 +954,7 @@ public final class SurfaceControl implements Parcelable {
/**
* The arguments class used to make layer capture requests.
*
- * @see #nativeCaptureLayers(LayerCaptureArgs)
+ * @see #nativeCaptureLayers(LayerCaptureArgs, ScreenCaptureListener)
* @hide
*/
public static class LayerCaptureArgs extends CaptureArgs {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5c0e15639491..9a412fcd436a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -41,6 +41,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.StyleRes;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.annotation.UiContext;
import android.annotation.UiThread;
@@ -9030,7 +9031,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* not be null or empty if a non-null listener is passed in.
* @param listener The listener to use. This can be null to reset to the default behavior.
*/
- public void setOnReceiveContentListener(@Nullable String[] mimeTypes,
+ public void setOnReceiveContentListener(
+ @SuppressLint("NullableCollection") @Nullable String[] mimeTypes,
@Nullable OnReceiveContentListener listener) {
if (listener != null) {
Preconditions.checkArgument(mimeTypes != null && mimeTypes.length > 0,
@@ -9106,6 +9108,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @return The MIME types accepted by {@link #performReceiveContent} for this view (may
* include patterns such as "image/*").
*/
+ @SuppressLint("NullableCollection")
@Nullable
public String[] getOnReceiveContentMimeTypes() {
return mOnReceiveContentMimeTypes;
@@ -21367,6 +21370,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int height = mBottom - mTop;
int layerType = getLayerType();
+ // Hacky hack: Reset any stretch effects as those are applied during the draw pass
+ // instead of being "stateful" like other RenderNode properties
+ renderNode.clearStretch();
+
final RecordingCanvas canvas = renderNode.beginRecording(width, height);
try {
@@ -22793,6 +22800,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
final Rect bounds = drawable.getBounds();
final int width = bounds.width();
final int height = bounds.height();
+
+ // Hacky hack: Reset any stretch effects as those are applied during the draw pass
+ // instead of being "stateful" like other RenderNode properties
+ renderNode.clearStretch();
+
final RecordingCanvas canvas = renderNode.beginRecording(width, height);
// Reverse left/top translation done by drawable canvas, which will
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index f5aa97a88608..8b3fb2e9e751 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -18,6 +18,7 @@ package android.view;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
@@ -377,7 +378,8 @@ public abstract class ViewStructure {
* <p>Should only be set when the node is used for Autofill or Content Capture purposes - it
* will be ignored when used for Assist.
*/
- public void setOnReceiveContentMimeTypes(@Nullable String[] mimeTypes) {}
+ public void setOnReceiveContentMimeTypes(
+ @SuppressLint("NullableCollection") @Nullable String[] mimeTypes) {}
/**
* Sets the {@link android.text.InputType} bits of this node.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index fa471fa28c7f..8319b74d9cdf 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -83,6 +83,7 @@ import static android.view.WindowLayoutParamsProto.Y;
import android.Manifest.permission;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -99,6 +100,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Build;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -2855,12 +2857,14 @@ public interface WindowManager extends ViewManager {
/**
* The token of {@link android.app.WindowContext}. It is usually a
- * {@link android.app.WindowTokenClient} and is used for updating
- * {@link android.content.res.Resources} from {@link Configuration} propagated from the
- * server side.
+ * {@link android.app.WindowTokenClient} and is used for associating the params with an
+ * existing node in the WindowManager hierarchy and getting the corresponding
+ * {@link Configuration} and {@link android.content.res.Resources} values with updates
+ * propagated from the server side.
*
* @hide
*/
+ @Nullable
public IBinder mWindowContextToken = null;
/**
@@ -3547,6 +3551,37 @@ public interface WindowManager extends ViewManager {
return userActivityTimeout;
}
+ /**
+ * Sets the {@link android.app.WindowContext} token.
+ *
+ * @see #getWindowContextToken()
+ *
+ * @hide
+ */
+ @TestApi
+ public final void setWindowContextToken(@NonNull IBinder token) {
+ mWindowContextToken = token;
+ }
+
+ /**
+ * Gets the {@link android.app.WindowContext} token.
+ *
+ * The token is usually a {@link android.app.WindowTokenClient} and is used for associating
+ * the params with an existing node in the WindowManager hierarchy and getting the
+ * corresponding {@link Configuration} and {@link android.content.res.Resources} values with
+ * updates propagated from the server side.
+ *
+ * @see android.app.WindowTokenClient
+ * @see Context#createWindowContext(Display, int, Bundle)
+ *
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ public final IBinder getWindowContextToken() {
+ return mWindowContextToken;
+ }
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index a76d46d1c00f..f3111bdc7471 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -858,7 +858,7 @@ public interface InputConnection {
boolean reportFullscreenMode(boolean enabled);
/**
- * Have the editor perform spell checking around the current selection.
+ * Have the editor perform spell checking for the full content.
*
* <p>The editor can ignore this method call if it does not support spell checking.
*
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 0a1aea38dd58..5980cb6c3671 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -187,7 +187,8 @@ public final class TextServicesManager {
* @return The spell checker session of the spell checker.
*/
@Nullable
- public SpellCheckerSession newSpellCheckerSession(@Nullable Bundle bundle,
+ public SpellCheckerSession newSpellCheckerSession(
+ @SuppressLint("NullableCollection") @Nullable Bundle bundle,
@SuppressLint("UseIcu") @Nullable Locale locale,
@NonNull SpellCheckerSessionListener listener,
@SuppressLint("ListenerLast") boolean referToSpellCheckerLanguageSettings,
@@ -277,6 +278,7 @@ public final class TextServicesManager {
* @return The list of currently enabled spell checkers.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public List<SpellCheckerInfo> getEnabledSpellCheckersList() {
final SpellCheckerInfo[] enabledSpellCheckers = getEnabledSpellCheckers();
return enabledSpellCheckers != null ? Arrays.asList(enabledSpellCheckers) : null;
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index ffdb89de5f59..98738eff83c3 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -16,6 +16,8 @@
package android.widget;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -25,8 +27,10 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
import android.view.View;
import android.widget.RemoteViews.RemoteView;
@@ -42,25 +46,32 @@ import java.time.ZoneId;
* @attr ref android.R.styleable#AnalogClock_dial
* @attr ref android.R.styleable#AnalogClock_hand_hour
* @attr ref android.R.styleable#AnalogClock_hand_minute
+ * @attr ref android.R.styleable#AnalogClock_hand_second
* @deprecated This widget is no longer supported.
*/
@RemoteView
@Deprecated
public class AnalogClock extends View {
+ /** How often the clock should refresh to make the seconds hand advance at ~15 FPS. */
+ private static final long SECONDS_TICK_FREQUENCY_MS = 1000 / 15;
+
private Clock mClock;
@UnsupportedAppUsage
private Drawable mHourHand;
@UnsupportedAppUsage
private Drawable mMinuteHand;
+ @Nullable
+ private Drawable mSecondHand;
@UnsupportedAppUsage
private Drawable mDial;
private int mDialWidth;
private int mDialHeight;
- private boolean mAttached;
+ private boolean mVisible;
+ private float mSeconds;
private float mMinutes;
private float mHour;
private boolean mChanged;
@@ -101,18 +112,70 @@ public class AnalogClock extends View {
mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute);
}
+ mSecondHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_second);
+
mClock = Clock.systemDefaultZone();
mDialWidth = mDial.getIntrinsicWidth();
mDialHeight = mDial.getIntrinsicHeight();
}
+ /** Sets the dial of the clock to the specified Icon. */
+ @RemotableViewMethod
+ public void setDial(@NonNull Icon icon) {
+ mDial = icon.loadDrawable(getContext());
+ mDialWidth = mDial.getIntrinsicWidth();
+ mDialHeight = mDial.getIntrinsicHeight();
+
+ mChanged = true;
+ invalidate();
+ }
+
+ /** Sets the hour hand of the clock to the specified Icon. */
+ @RemotableViewMethod
+ public void setHourHand(@NonNull Icon icon) {
+ mHourHand = icon.loadDrawable(getContext());
+
+ mChanged = true;
+ invalidate();
+ }
+
+ /** Sets the minute hand of the clock to the specified Icon. */
+ @RemotableViewMethod
+ public void setMinuteHand(@NonNull Icon icon) {
+ mMinuteHand = icon.loadDrawable(getContext());
+
+ mChanged = true;
+ invalidate();
+ }
+
+ /**
+ * Sets the second hand of the clock to the specified Icon, or hides the second hand if it is
+ * null.
+ */
+ @RemotableViewMethod
+ public void setSecondHand(@Nullable Icon icon) {
+ mSecondHand = icon == null ? null : icon.loadDrawable(getContext());
+ mSecondsTick.run();
+
+ mChanged = true;
+ invalidate();
+ }
+
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
- if (!mAttached) {
- mAttached = true;
+ if (isVisible) {
+ onVisible();
+ } else {
+ onInvisible();
+ }
+ }
+
+ private void onVisible() {
+ if (!mVisible) {
+ mVisible = true;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
@@ -128,6 +191,8 @@ public class AnalogClock extends View {
// user not the one the context is for.
getContext().registerReceiverAsUser(mIntentReceiver,
android.os.Process.myUserHandle(), filter, null, getHandler());
+
+ mSecondsTick.run();
}
// NOTE: It's safe to do these after registering the receiver since the receiver always runs
@@ -140,12 +205,11 @@ public class AnalogClock extends View {
onTimeChanged();
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mAttached) {
+ private void onInvisible() {
+ if (mVisible) {
getContext().unregisterReceiver(mIntentReceiver);
- mAttached = false;
+ removeCallbacks(mSecondsTick);
+ mVisible = false;
}
}
@@ -237,6 +301,20 @@ public class AnalogClock extends View {
minuteHand.draw(canvas);
canvas.restore();
+ final Drawable secondHand = mSecondHand;
+ if (secondHand != null) {
+ canvas.save();
+ canvas.rotate(mSeconds / 60.0f * 360.0f, x, y);
+
+ if (changed) {
+ w = secondHand.getIntrinsicWidth();
+ h = secondHand.getIntrinsicHeight();
+ secondHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
+ }
+ secondHand.draw(canvas);
+ canvas.restore();
+ }
+
if (scaled) {
canvas.restore();
}
@@ -250,6 +328,7 @@ public class AnalogClock extends View {
int minute = localDateTime.getMinute();
int second = localDateTime.getSecond();
+ mSeconds = second + localDateTime.getNano() / 1_000_000_000f;
mMinutes = minute + second / 60.0f;
mHour = hour + mMinutes / 60.0f;
mChanged = true;
@@ -271,6 +350,21 @@ public class AnalogClock extends View {
}
};
+ private final Runnable mSecondsTick = new Runnable() {
+ @Override
+ public void run() {
+ if (!mVisible || mSecondHand == null) {
+ return;
+ }
+
+ onTimeChanged();
+
+ invalidate();
+
+ postDelayed(this, SECONDS_TICK_FREQUENCY_MS);
+ }
+ };
+
private void updateContentDescription(long timeMillis) {
final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR;
String contentDescription = DateUtils.formatDateTime(mContext, timeMillis, flags);
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 97d98fd8ec59..794b642135d0 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -220,29 +220,26 @@ public class SpellChecker implements SpellCheckerSessionListener {
}
void onPerformSpellCheck() {
- final int selectionStart = mTextView.getSelectionStart();
- final int selectionEnd = mTextView.getSelectionEnd();
- final int selectionRangeStart;
- final int selectionRangeEnd;
- if (selectionStart < selectionEnd) {
- selectionRangeStart = selectionStart;
- selectionRangeEnd = selectionEnd;
- } else {
- selectionRangeStart = selectionEnd;
- selectionRangeEnd = selectionStart;
- }
- // Expand the range so that it (hopefully) includes the current sentence.
- final int start = Math.max(0, selectionRangeStart - MIN_SENTENCE_LENGTH);
- final int end = Math.min(mTextView.length(), selectionRangeEnd + MIN_SENTENCE_LENGTH);
+ // Triggers full content spell check.
+ final int start = 0;
+ final int end = mTextView.length();
if (DBG) {
Log.d(TAG, "performSpellCheckAroundSelection: " + start + ", " + end);
}
- spellCheck(start, end);
+ spellCheck(start, end, /* forceCheckWhenEditingWord= */ true);
}
public void spellCheck(int start, int end) {
+ spellCheck(start, end, /* forceCheckWhenEditingWord= */ false);
+ }
+
+ /**
+ * Requests to do spell check for text in the range (start, end).
+ */
+ public void spellCheck(int start, int end, boolean forceCheckWhenEditingWord) {
if (DBG) {
- Log.d(TAG, "Start spell-checking: " + start + ", " + end);
+ Log.d(TAG, "Start spell-checking: " + start + ", " + end + ", "
+ + forceCheckWhenEditingWord);
}
final Locale locale = mTextView.getSpellCheckerLocale();
final boolean isSessionActive = isSessionActive();
@@ -267,7 +264,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
for (int i = 0; i < length; i++) {
final SpellParser spellParser = mSpellParsers[i];
if (spellParser.isFinished()) {
- spellParser.parse(start, end);
+ spellParser.parse(start, end, forceCheckWhenEditingWord);
return;
}
}
@@ -282,10 +279,14 @@ public class SpellChecker implements SpellCheckerSessionListener {
SpellParser spellParser = new SpellParser();
mSpellParsers[length] = spellParser;
- spellParser.parse(start, end);
+ spellParser.parse(start, end, forceCheckWhenEditingWord);
}
private void spellCheck() {
+ spellCheck(/* forceCheckWhenEditingWord= */ false);
+ }
+
+ private void spellCheck(boolean forceCheckWhenEditingWord) {
if (mSpellCheckerSession == null) return;
Editable editable = (Editable) mTextView.getText();
@@ -295,6 +296,12 @@ public class SpellChecker implements SpellCheckerSessionListener {
TextInfo[] textInfos = new TextInfo[mLength];
int textInfosCount = 0;
+ if (DBG) {
+ Log.d(TAG, "forceCheckWhenEditingWord=" + forceCheckWhenEditingWord
+ + ", mLength=" + mLength + ", cookie = " + mCookie
+ + ", sel start = " + selectionStart + ", sel end = " + selectionEnd);
+ }
+
for (int i = 0; i < mLength; i++) {
final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
if (mIds[i] < 0 || spellCheckSpan.isSpellCheckInProgress()) continue;
@@ -319,7 +326,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
} else {
isEditing = selectionEnd < start || selectionStart > end;
}
- if (start >= 0 && end > start && isEditing) {
+ if (start >= 0 && end > start && (forceCheckWhenEditingWord || isEditing)) {
spellCheckSpan.setSpellCheckInProgress(true);
final TextInfo textInfo = new TextInfo(editable, start, end, mCookie, mIds[i]);
textInfos[textInfosCount++] = textInfo;
@@ -546,7 +553,11 @@ public class SpellChecker implements SpellCheckerSessionListener {
private class SpellParser {
private Object mRange = new Object();
- public void parse(int start, int end) {
+ // Forces to do spell checker even user is editing the word.
+ private boolean mForceCheckWhenEditingWord;
+
+ public void parse(int start, int end, boolean forceCheckWhenEditingWord) {
+ mForceCheckWhenEditingWord = forceCheckWhenEditingWord;
final int max = mTextView.length();
final int parseEnd;
if (end > max) {
@@ -567,6 +578,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
public void stop() {
removeRangeSpan((Editable) mTextView.getText());
+ mForceCheckWhenEditingWord = false;
}
private void setRangeSpan(Editable editable, int start, int end) {
@@ -617,7 +629,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
if (DBG) {
Log.i(TAG, "No more spell check.");
}
- removeRangeSpan(editable);
+ stop();
return;
}
@@ -649,7 +661,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
if (DBG) {
Log.i(TAG, "Incorrect range span.");
}
- removeRangeSpan(editable);
+ stop();
return;
}
do {
@@ -778,7 +790,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
removeRangeSpan(editable);
}
- spellCheck();
+ spellCheck(mForceCheckWhenEditingWord);
}
private <T> void removeSpansAt(Editable editable, int offset, T[] spans) {
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index f29eb39045ae..cdb4762a4f0a 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -21,6 +21,7 @@ import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.os.IBinder;
@@ -151,6 +152,7 @@ public class TaskOrganizer extends WindowOrganizer {
/** Gets direct child tasks (ordered from top-to-bottom) */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
@Nullable
+ @SuppressLint("NullableCollection")
public List<ActivityManager.RunningTaskInfo> getChildTasks(
@NonNull WindowContainerToken parent, @NonNull int[] activityTypes) {
try {
@@ -163,6 +165,7 @@ public class TaskOrganizer extends WindowOrganizer {
/** Gets all root tasks on a display (ordered from top-to-bottom) */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
@Nullable
+ @SuppressLint("NullableCollection")
public List<ActivityManager.RunningTaskInfo> getRootTasks(
int displayId, @NonNull int[] activityTypes) {
try {
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index 99692d0736c2..7ade05cc6de1 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -5,3 +5,4 @@ per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS
per-file IVoice* = file:/core/java/android/service/voice/OWNERS
per-file *Hotword* = file:/core/java/android/service/voice/OWNERS
+per-file *BatteryStats* = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 13358daf05a1..1f09489b3f1f 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -62,6 +62,11 @@ public final class SystemUiDeviceConfigFlags {
*/
public static final String ENABLE_NAS_FEEDBACK = "enable_nas_feedback";
+ /**
+ * Whether the Notification Assistant can label a notification not a conversation
+ */
+ public static final String ENABLE_NAS_NOT_CONVERSATION = "enable_nas_not_conversation";
+
// Flags related to screenshot intelligence
/**
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index b8c066db29af..ea2fb88a3fc7 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -18,7 +18,6 @@ package com.android.internal.os;
import android.content.Context;
import android.hardware.SensorManager;
-import android.net.ConnectivityManager;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
@@ -57,7 +56,7 @@ public class BatteryUsageStatsProvider {
mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile));
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
- if (!isWifiOnlyDevice(mContext)) {
+ if (!BatteryStatsHelper.checkWifiOnly(mContext)) {
mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
}
mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
@@ -81,14 +80,6 @@ public class BatteryUsageStatsProvider {
return mPowerCalculators;
}
- private static boolean isWifiOnlyDevice(Context context) {
- ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
- if (cm == null) {
- return false;
- }
- return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
- }
-
/**
* Returns a snapshot of battery attribution data.
*/
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 892c5a53527b..50bbfc5ccb08 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -91,11 +91,12 @@ interface IInputMethodManager {
/** Remove the IME surface. Requires passing the currently focused window. */
oneway void removeImeSurfaceFromWindow(in IBinder windowToken,
in IVoidResultCallback resultCallback);
- void startProtoDump(in byte[] protoDump, int source, String where);
+ oneway void startProtoDump(in byte[] protoDump, int source, String where,
+ in IVoidResultCallback resultCallback);
oneway void isImeTraceEnabled(in IBooleanResultCallback resultCallback);
// Starts an ime trace.
- void startImeTrace();
+ oneway void startImeTrace(in IVoidResultCallback resultCallback);
// Stops an ime trace.
- void stopImeTrace();
+ oneway void stopImeTrace(in IVoidResultCallback resultCallback);
}
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 4eaa016df6f2..9cc72437a023 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -70,7 +70,8 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
deviceInfo.isExternal(), deviceInfo.getSources(),
deviceInfo.getKeyboardType(), kcmObj.get(),
deviceInfo.hasVibrator(), hasMic,
- deviceInfo.hasButtonUnderPad(), deviceInfo.hasSensor()));
+ deviceInfo.hasButtonUnderPad(), deviceInfo.hasSensor(),
+ deviceInfo.hasBattery()));
const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
for (const InputDeviceInfo::MotionRange& range: ranges) {
@@ -90,9 +91,10 @@ int register_android_view_InputDevice(JNIEnv* env)
gInputDeviceClassInfo.clazz = FindClassOrDie(env, "android/view/InputDevice");
gInputDeviceClassInfo.clazz = MakeGlobalRefOrDie(env, gInputDeviceClassInfo.clazz);
- gInputDeviceClassInfo.ctor = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
- "(IIILjava/lang/String;IILjava/lang/"
- "String;ZIILandroid/view/KeyCharacterMap;ZZZZ)V");
+ gInputDeviceClassInfo.ctor =
+ GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
+ "(IIILjava/lang/String;IILjava/lang/"
+ "String;ZIILandroid/view/KeyCharacterMap;ZZZZZ)V");
gInputDeviceClassInfo.addMotionRange = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz,
"addMotionRange", "(IIFFFFF)V");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 4ef63ae93016..05fcaec82f84 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -27,6 +27,7 @@
#include <android-base/chrono_utils.h>
#include <android/graphics/region.h>
#include <android/gui/BnScreenCaptureListener.h>
+#include <android/os/IInputConstants.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <android_runtime/android_view_Surface.h>
@@ -110,10 +111,12 @@ static struct {
static struct {
jfieldID pixelFormat;
jfieldID sourceCrop;
- jfieldID frameScale;
+ jfieldID frameScaleX;
+ jfieldID frameScaleY;
jfieldID captureSecureLayers;
jfieldID allowProtected;
jfieldID uid;
+ jfieldID grayscale;
} gCaptureArgsClassInfo;
static struct {
@@ -380,13 +383,17 @@ static void getCaptureArgs(JNIEnv* env, jobject captureArgsObject, CaptureArgs&
captureArgs.sourceCrop =
rectFromObj(env,
env->GetObjectField(captureArgsObject, gCaptureArgsClassInfo.sourceCrop));
- captureArgs.frameScale =
- env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScale);
+ captureArgs.frameScaleX =
+ env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleX);
+ captureArgs.frameScaleY =
+ env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleY);
captureArgs.captureSecureLayers =
env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.captureSecureLayers);
captureArgs.allowProtected =
env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.allowProtected);
captureArgs.uid = env->GetLongField(captureArgsObject, gCaptureArgsClassInfo.uid);
+ captureArgs.grayscale =
+ env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.grayscale);
}
static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
@@ -1619,7 +1626,8 @@ static void nativeSetFrameTimelineVsync(JNIEnv* env, jclass clazz, jlong transac
jlong frameTimelineVsyncId) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- transaction->setFrameTimelineVsync(frameTimelineVsyncId);
+ transaction->setFrameTimelineInfo(
+ {frameTimelineVsyncId, android::os::IInputConstants::INVALID_INPUT_EVENT_ID});
}
class JankDataListenerWrapper : public JankDataListener {
@@ -2032,12 +2040,14 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gCaptureArgsClassInfo.pixelFormat = GetFieldIDOrDie(env, captureArgsClazz, "mPixelFormat", "I");
gCaptureArgsClassInfo.sourceCrop =
GetFieldIDOrDie(env, captureArgsClazz, "mSourceCrop", "Landroid/graphics/Rect;");
- gCaptureArgsClassInfo.frameScale = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScale", "F");
+ gCaptureArgsClassInfo.frameScaleX = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScaleX", "F");
+ gCaptureArgsClassInfo.frameScaleY = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScaleY", "F");
gCaptureArgsClassInfo.captureSecureLayers =
GetFieldIDOrDie(env, captureArgsClazz, "mCaptureSecureLayers", "Z");
gCaptureArgsClassInfo.allowProtected =
GetFieldIDOrDie(env, captureArgsClazz, "mAllowProtected", "Z");
gCaptureArgsClassInfo.uid = GetFieldIDOrDie(env, captureArgsClazz, "mUid", "J");
+ gCaptureArgsClassInfo.grayscale = GetFieldIDOrDie(env, captureArgsClazz, "mGrayscale", "Z");
jclass displayCaptureArgsClazz =
FindClassOrDie(env, "android/view/SurfaceControl$DisplayCaptureArgs");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index c76a5d3dd9fa..c5a955976867 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -70,7 +70,6 @@
#include <bionic/malloc.h>
#include <bionic/mte.h>
#include <cutils/fs.h>
-#include <cutils/memory.h>
#include <cutils/multiuser.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
@@ -621,13 +620,6 @@ static void PreApplicationInit() {
// Set the jemalloc decay time to 1.
mallopt(M_DECAY_TIME, 1);
-
- // Avoid potentially expensive memory mitigations, mostly meant for system
- // processes, in apps. These may cause app compat problems, use more memory,
- // or reduce performance. While it would be nice to have them for apps,
- // we will have to wait until they are proven out, have more efficient
- // hardware, and/or apply them only to new applications.
- process_disable_memory_mitigations();
}
static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
@@ -1785,6 +1777,14 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
break;
}
mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level);
+
+ // Avoid heap zero initialization for applications without MTE. Zero init may
+ // cause app compat problems, use more memory, or reduce performance. While it
+ // would be nice to have them for apps, we will have to wait until they are
+ // proven out, have more efficient hardware, and/or apply them only to new
+ // applications.
+ mallopt(M_BIONIC_ZERO_INIT, 0);
+
// Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4acdb1663403..f447cd205516 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5168,6 +5168,11 @@
<permission android:name="android.permission.MANAGE_SEARCH_UI"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an application to manage the smartspace service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_SMARTSPACE"
+ android:protectionLevel="signature" />
+
<!-- Allows an app to set the theme overlay in /vendor/overlay
being used.
@hide <p>Not for use by third-party applications.</p> -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 14f1e0e20ef6..07c3adfb4c69 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4069,6 +4069,7 @@
<attr name="dial" format="reference"/>
<attr name="hand_hour" format="reference"/>
<attr name="hand_minute" format="reference"/>
+ <attr name="hand_second" format="reference"/>
</declare-styleable>
<declare-styleable name="Button">
</declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5e0cda69911b..01b8efafa2fd 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3800,6 +3800,15 @@
-->
<string name="config_defaultSearchUiService" translatable="false"></string>
+ <!-- The package name for the system's smartspace service.
+ This service returns smartspace results.
+
+ This service must be trusted, as it can be activated without explicit consent of the user.
+ If no service with the specified name exists on the device, smartspace will be disabled.
+ Example: "com.android.intelligence/.SmartspaceService"
+-->
+ <string name="config_defaultSmartspaceService" translatable="false"></string>
+
<!-- The package name for the system's speech recognition service.
This service must be trusted, as it can be activated without explicit consent of the user.
Example: "com.android.speech/.RecognitionService"
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9c1c51cdd48e..b76ab3a22a20 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3059,6 +3059,7 @@
<public name="pathAdvancedPattern" />
<public name="sspAdvancedPattern" />
<public name="fontProviderSystemFontFamily" />
+ <public name="hand_second" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dfccdf4bd9a5..815330ff1851 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3498,6 +3498,7 @@
<java-symbol type="string" name="config_defaultAppPredictionService" />
<java-symbol type="string" name="config_defaultContentSuggestionsService" />
<java-symbol type="string" name="config_defaultSearchUiService" />
+ <java-symbol type="string" name="config_defaultSmartspaceService" />
<java-symbol type="string" name="config_defaultMusicRecognitionService" />
<java-symbol type="string" name="config_defaultAttentionService" />
<java-symbol type="string" name="config_defaultRotationResolverService" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index bb826deb4eff..151a320494b4 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -35,6 +35,8 @@
android:label="@string/permlab_testDenied"
android:description="@string/permdesc_testDenied" />
+ <uses-permission android:name="com.android.frameworks.coretests.permission.TEST_GRANTED" />
+
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED" />
@@ -76,7 +78,6 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SMS"/>
- <uses-permission android:name="android.permission.TEST_GRANTED" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
@@ -1455,6 +1456,7 @@
</intent-filter>
</receiver>
<receiver android:name="android.app.activity.RemoteGrantedReceiver"
+ android:process=":remoteReceiver"
android:permission="com.android.frameworks.coretests.permission.TEST_GRANTED"
android:exported="true">
<intent-filter android:priority="2">
@@ -1462,6 +1464,7 @@
</intent-filter>
</receiver>
<receiver android:name="android.app.activity.RemoteDeniedReceiver"
+ android:process=":remoteReceiver"
android:permission="com.android.frameworks.coretests.permission.TEST_DENIED"
android:exported="true">
<intent-filter android:priority="2">
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index 0f81896692c0..d79c2fe19ce2 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -56,6 +56,8 @@ public class BroadcastTest extends ActivityTestsBase {
"com.android.frameworks.coretests.activity.BROADCAST_MULTI";
public static final String BROADCAST_ABORT =
"com.android.frameworks.coretests.activity.BROADCAST_ABORT";
+ public static final String BROADCAST_RESULT =
+ "com.android.frameworks.coretests.activity.BROADCAST_RESULT";
public static final String BROADCAST_STICKY1 =
"com.android.frameworks.coretests.activity.BROADCAST_STICKY1";
@@ -106,7 +108,14 @@ public class BroadcastTest extends ActivityTestsBase {
}
public Intent makeBroadcastIntent(String action) {
+ return makeBroadcastIntent(action, false);
+ }
+
+ public Intent makeBroadcastIntent(String action, boolean makeImplicit) {
Intent intent = new Intent(action, null);
+ if (makeImplicit) {
+ intent.addFlags(intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ }
intent.putExtra("caller", mCallTarget);
return intent;
}
@@ -277,7 +286,7 @@ public class BroadcastTest extends ActivityTestsBase {
map.putString("foo", "you");
map.putString("remove", "me");
getContext().sendOrderedBroadcast(
- new Intent("com.android.frameworks.coretests.activity.BROADCAST_RESULT"),
+ makeBroadcastIntent(BROADCAST_RESULT, true),
null, broadcastReceiver, null, 1, "foo", map);
while (!broadcastReceiver.mHaveResult) {
try {
@@ -424,10 +433,13 @@ public class BroadcastTest extends ActivityTestsBase {
public void testLocalReceivePermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL_GRANTED));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL_GRANTED, true));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
+ /*
+ // TODO: multi-package test b/c self-target broadcasts are always allowed
+ // even when gated on ungranted permissions
public void testLocalReceivePermissionDenied() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_RESULTS});
@@ -438,16 +450,17 @@ public class BroadcastTest extends ActivityTestsBase {
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_LOCAL_DENIED),
+ makeBroadcastIntent(BROADCAST_LOCAL_DENIED, true),
null, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
+ */
public void testLocalBroadcastPermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
getContext().sendBroadcast(
- makeBroadcastIntent(BROADCAST_LOCAL),
+ makeBroadcastIntent(BROADCAST_LOCAL, true),
PERMISSION_GRANTED);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
@@ -462,7 +475,7 @@ public class BroadcastTest extends ActivityTestsBase {
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_LOCAL),
+ makeBroadcastIntent(BROADCAST_LOCAL, true),
PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
@@ -470,10 +483,13 @@ public class BroadcastTest extends ActivityTestsBase {
public void testRemoteReceivePermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_REMOTE});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE_GRANTED));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE_GRANTED, true));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
+ /*
+ // TODO: multi-package test b/c self-target broadcasts are always allowed
+ // even when gated on ungranted permissions
public void testRemoteReceivePermissionDenied() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_RESULTS});
@@ -484,16 +500,17 @@ public class BroadcastTest extends ActivityTestsBase {
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_REMOTE_DENIED),
+ makeBroadcastIntent(BROADCAST_REMOTE_DENIED, true),
null, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
+ */
public void testRemoteBroadcastPermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_REMOTE});
getContext().sendBroadcast(
- makeBroadcastIntent(BROADCAST_REMOTE),
+ makeBroadcastIntent(BROADCAST_REMOTE, true),
PERMISSION_GRANTED);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
@@ -508,7 +525,7 @@ public class BroadcastTest extends ActivityTestsBase {
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_REMOTE),
+ makeBroadcastIntent(BROADCAST_REMOTE, true),
PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
@@ -516,13 +533,13 @@ public class BroadcastTest extends ActivityTestsBase {
public void testReceiverCanNotRegister() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_REGISTER));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_REGISTER, true));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
public void testReceiverCanNotBind() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_BIND));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_BIND, true));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
diff --git a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
index 766245600d13..0b21fa90c1c4 100644
--- a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
+++ b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
@@ -253,16 +253,16 @@ public class LaunchpadActivity extends Activity {
sendBroadcast(makeBroadcastIntent(BROADCAST_REGISTERED));
} else if (BROADCAST_LOCAL.equals(action)) {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL));
+ sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL, true));
} else if (BROADCAST_REMOTE.equals(action)) {
setExpectedReceivers(new String[]{RECEIVER_REMOTE});
- sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE));
+ sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE, true));
} else if (BROADCAST_ALL.equals(action)) {
setExpectedReceivers(new String[]{
RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL});
registerMyReceiver(new IntentFilter(BROADCAST_ALL));
sCallingTest.addIntermediate("after-register");
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL, true), null);
} else if (BROADCAST_MULTI.equals(action)) {
setExpectedReceivers(new String[]{
RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
@@ -277,23 +277,26 @@ public class LaunchpadActivity extends Activity {
RECEIVER_REMOTE, RECEIVER_LOCAL});
registerMyReceiver(new IntentFilter(BROADCAST_ALL));
sCallingTest.addIntermediate("after-register");
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REPEAT), null);
+ final Intent allIntent = makeBroadcastIntent(BROADCAST_ALL, true);
+ final Intent localIntent = makeBroadcastIntent(BROADCAST_LOCAL, true);
+ final Intent remoteIntent = makeBroadcastIntent(BROADCAST_REMOTE, true);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(localIntent, null);
+ sendOrderedBroadcast(remoteIntent, null);
+ sendOrderedBroadcast(localIntent, null);
+ sendOrderedBroadcast(remoteIntent, null);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(allIntent, null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REPEAT, true), null);
} else if (BROADCAST_ABORT.equals(action)) {
setExpectedReceivers(new String[]{
RECEIVER_REMOTE, RECEIVER_ABORT});
registerMyReceiver(new IntentFilter(BROADCAST_ABORT));
sCallingTest.addIntermediate("after-register");
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ABORT), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ABORT, true), null);
} else if (BROADCAST_STICKY1.equals(action)) {
setExpectedReceivers(new String[]{RECEIVER_REG});
setExpectedData(new String[]{DATA_1});
@@ -436,7 +439,14 @@ public class LaunchpadActivity extends Activity {
}
private Intent makeBroadcastIntent(String action) {
+ return makeBroadcastIntent(action, false);
+ }
+
+ private Intent makeBroadcastIntent(String action, boolean makeImplicit) {
Intent intent = new Intent(action, null);
+ if (makeImplicit) {
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ }
intent.putExtra("caller", mCallTarget);
return intent;
}
diff --git a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
index 2120a1db463c..16ea73f30937 100644
--- a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
+++ b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
@@ -23,7 +23,7 @@ import android.os.RemoteException;
import android.os.IBinder;
import android.os.Parcel;
-class LocalDeniedReceiver extends BroadcastReceiver {
+public class LocalDeniedReceiver extends BroadcastReceiver {
public LocalDeniedReceiver() {
}
diff --git a/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
index 7c89346e820d..5c1ded93e1c3 100644
--- a/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
+++ b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
@@ -23,7 +23,7 @@ import android.os.RemoteException;
import android.os.IBinder;
import android.os.Parcel;
-class RemoteDeniedReceiver extends BroadcastReceiver {
+public class RemoteDeniedReceiver extends BroadcastReceiver {
public RemoteDeniedReceiver() {
}
diff --git a/core/tests/coretests/src/android/content/pm/SignatureTest.java b/core/tests/coretests/src/android/content/pm/SignatureTest.java
index f0b4af6fc44f..19458da796e8 100644
--- a/core/tests/coretests/src/android/content/pm/SignatureTest.java
+++ b/core/tests/coretests/src/android/content/pm/SignatureTest.java
@@ -54,6 +54,32 @@ public class SignatureTest extends TestCase {
assertFalse(Signature.areEffectiveMatch(asArray(A, M), asArray(A, B)));
}
+ public void testHashCode_doesNotIncludeFlags() throws Exception {
+ // Some classes rely on the hash code not including the flags / capabilities for the signer
+ // to verify Set membership. This test verifies two signers with the same signature but
+ // different flags have the same hash code.
+ Signature signatureAWithAllCaps = new Signature(A.toCharsString());
+ // There are currently 5 capabilities that can be assigned to a previous signer, although
+ // for the purposes of this test all that matters is that the two flag values are distinct.
+ signatureAWithAllCaps.setFlags(31);
+ Signature signatureAWithNoCaps = new Signature(A.toCharsString());
+ signatureAWithNoCaps.setFlags(0);
+
+ assertEquals(signatureAWithAllCaps.hashCode(), signatureAWithNoCaps.hashCode());
+ }
+
+ public void testEquals_doesNotIncludeFlags() throws Exception {
+ // Similar to above some classes rely on equals only comparing the signature arrays
+ // for equality without including the flags. This test verifies two signers with the
+ // same signature but different flags are still considered equal.
+ Signature signatureAWithAllCaps = new Signature(A.toCharsString());
+ signatureAWithAllCaps.setFlags(31);
+ Signature signatureAWithNoCaps = new Signature(A.toCharsString());
+ signatureAWithNoCaps.setFlags(0);
+
+ assertEquals(signatureAWithAllCaps, signatureAWithNoCaps);
+ }
+
private static Signature[] asArray(Signature... s) {
return s;
}
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
index b319886ae466..341ee37aee39 100644
--- a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
@@ -149,7 +149,7 @@ public class InputDeviceSensorManagerTest {
0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
0 /* sources */, 0 /* keyboardType */, null /* keyCharacterMap */,
false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */,
- true /* hasSensor */);
+ true /* hasSensor */, false /* hasBattery */);
assertTrue(d.hasSensor());
return d;
}
diff --git a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
index 6955ca84103e..564103efef65 100644
--- a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
@@ -117,6 +117,61 @@ public class CombinedVibrationEffectTest {
}
@Test
+ public void testDurationMono() {
+ assertEquals(1, CombinedVibrationEffect.createSynced(
+ VibrationEffect.createOneShot(1, 1)).getDuration());
+ assertEquals(-1, CombinedVibrationEffect.createSynced(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration());
+ assertEquals(Long.MAX_VALUE, CombinedVibrationEffect.createSynced(
+ VibrationEffect.createWaveform(
+ new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0)).getDuration());
+ }
+
+ @Test
+ public void testDurationStereo() {
+ assertEquals(6, CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.createOneShot(1, 1))
+ .addVibrator(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, -1))
+ .combine()
+ .getDuration());
+ assertEquals(-1, CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, -1))
+ .combine()
+ .getDuration());
+ assertEquals(Long.MAX_VALUE, CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0))
+ .combine()
+ .getDuration());
+ }
+
+ @Test
+ public void testDurationSequential() {
+ assertEquals(26, CombinedVibrationEffect.startSequential()
+ .addNext(1, VibrationEffect.createOneShot(10, 10), 10)
+ .addNext(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, -1))
+ .combine()
+ .getDuration());
+ assertEquals(-1, CombinedVibrationEffect.startSequential()
+ .addNext(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addNext(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, -1))
+ .combine()
+ .getDuration());
+ assertEquals(Long.MAX_VALUE, CombinedVibrationEffect.startSequential()
+ .addNext(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addNext(2,
+ VibrationEffect.createWaveform(new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0))
+ .combine()
+ .getDuration());
+ }
+
+ @Test
public void testSerializationMono() {
CombinedVibrationEffect original = CombinedVibrationEffect.createSynced(VALID_EFFECT);
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index c1310a9214e0..4a92cf11fa5c 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -693,6 +693,32 @@ public final class RenderNode {
throw new IllegalArgumentException("Unrecognized outline?");
}
+ /** @hide */
+ public boolean clearStretch() {
+ return nClearStretch(mNativeRenderNode);
+ }
+
+ /** @hide */
+ public boolean stretch(float left, float top, float right, float bottom,
+ float vecX, float vecY, float maxStretchAmount) {
+ if (1.0 < vecX || vecX < -1.0) {
+ throw new IllegalArgumentException("vecX must be in the range [-1, 1], was " + vecX);
+ }
+ if (1.0 < vecY || vecY < -1.0) {
+ throw new IllegalArgumentException("vecY must be in the range [-1, 1], was " + vecY);
+ }
+ if (top <= bottom || right <= left) {
+ throw new IllegalArgumentException(
+ "Stretch region must not be empty, got "
+ + new RectF(left, top, right, bottom).toString());
+ }
+ if (maxStretchAmount <= 0.0f) {
+ throw new IllegalArgumentException(
+ "The max stretch amount must be >0, got " + maxStretchAmount);
+ }
+ return nStretch(mNativeRenderNode, left, top, right, bottom, vecX, vecY, maxStretchAmount);
+ }
+
/**
* Checks if the RenderNode has a shadow. That is, if the combination of {@link #getElevation()}
* and {@link #getTranslationZ()} is greater than zero, there is an {@link Outline} set with
@@ -1638,6 +1664,13 @@ public final class RenderNode {
private static native boolean nSetOutlineNone(long renderNode);
@CriticalNative
+ private static native boolean nClearStretch(long renderNode);
+
+ @CriticalNative
+ private static native boolean nStretch(long renderNode, float left, float top, float right,
+ float bottom, float vecX, float vecY, float maxStretch);
+
+ @CriticalNative
private static native boolean nHasShadow(long renderNode);
@CriticalNative
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index f41b6081e38c..ae9f866459d6 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -19,9 +19,9 @@ package android.security;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
-import com.android.org.bouncycastle.util.io.pem.PemObject;
-import com.android.org.bouncycastle.util.io.pem.PemReader;
-import com.android.org.bouncycastle.util.io.pem.PemWriter;
+import com.android.internal.org.bouncycastle.util.io.pem.PemObject;
+import com.android.internal.org.bouncycastle.util.io.pem.PemReader;
+import com.android.internal.org.bouncycastle.util.io.pem.PemWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 4a67135227dd..e19d88c182ff 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -45,8 +45,8 @@ import android.security.keystore.KeystoreResponse;
import android.security.keystore.UserNotAuthenticatedException;
import android.util.Log;
-import com.android.org.bouncycastle.asn1.ASN1InputStream;
-import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import java.io.ByteArrayInputStream;
import java.io.IOException;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 6ad8d2c0aca3..334b1110d651 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -26,24 +26,24 @@ import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keymaster.KeymasterDefs;
-import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
-import com.android.org.bouncycastle.asn1.ASN1InputStream;
-import com.android.org.bouncycastle.asn1.ASN1Integer;
-import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import com.android.org.bouncycastle.asn1.DERBitString;
-import com.android.org.bouncycastle.asn1.DERNull;
-import com.android.org.bouncycastle.asn1.DERSequence;
-import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import com.android.org.bouncycastle.asn1.x509.Certificate;
-import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import com.android.org.bouncycastle.asn1.x509.TBSCertificate;
-import com.android.org.bouncycastle.asn1.x509.Time;
-import com.android.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
-import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
-import com.android.org.bouncycastle.jce.X509Principal;
-import com.android.org.bouncycastle.jce.provider.X509CertificateObject;
-import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.DERBitString;
+import com.android.internal.org.bouncycastle.asn1.DERNull;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.TBSCertificate;
+import com.android.internal.org.bouncycastle.asn1.x509.Time;
+import com.android.internal.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
+import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import com.android.internal.org.bouncycastle.jce.X509Principal;
+import com.android.internal.org.bouncycastle.jce.provider.X509CertificateObject;
+import com.android.internal.org.bouncycastle.x509.X509V3CertificateGenerator;
import libcore.util.EmptyArray;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 164bc8669525..75ac61a22cab 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -363,6 +363,11 @@ public class AndroidKeyStoreProvider extends Provider {
}
}
+ if (response.iSecurityLevel == null) {
+ // This seems to be a pure certificate entry, nothing to return here.
+ return null;
+ }
+
Integer keymasterAlgorithm = null;
// We just need one digest for the algorithm name
int keymasterDigest = -1;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 5e7f6482ebed..07169cedc1d9 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -490,7 +490,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
int[] keymasterEncryptionPaddings =
KeyProperties.EncryptionPadding.allToKeymaster(
spec.getEncryptionPaddings());
- if (((spec.getPurposes() & KeyProperties.PURPOSE_DECRYPT) != 0)
+ if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
&& (spec.isRandomizedEncryptionRequired())) {
for (int keymasterPadding : keymasterEncryptionPaddings) {
if (!KeymasterUtils
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 128d13c2ce2e..bb6704333050 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -30,7 +30,6 @@ import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.os.Handler;
import android.provider.DeviceConfig;
import android.util.Log;
import android.util.Size;
@@ -847,11 +846,11 @@ public class PipTouchHandler {
&& mPipBoundsState.getBounds().height()
< mPipBoundsState.getMaxSize().y;
if (toExpand) {
+ mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
animateToMaximizedState(null);
} else {
- animateToMinimizedState();
+ animateToUnexpandedState(getUserResizeBounds());
}
- mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
} else {
// Expand to fullscreen if this is a double tap
// the PiP should be frozen until the transition ends
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index ccfdce65f7b2..24b0f3043319 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -18,10 +18,11 @@ package com.android.wm.shell.flicker
import android.graphics.Region
import android.view.Surface
+import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.FlickerTestBase.Companion.DOCKED_STACK_DIVIDER
@JvmOverloads
fun LayersAssertionBuilder.appPairsDividerIsVisible(
@@ -29,7 +30,7 @@ fun LayersAssertionBuilder.appPairsDividerIsVisible(
enabled: Boolean = bugId == 0
) {
end("appPairsDividerIsVisible", bugId, enabled) {
- this.isVisible(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
+ this.isVisible(APP_PAIR_SPLIT_DIVIDER)
}
}
@@ -39,7 +40,7 @@ fun LayersAssertionBuilder.appPairsDividerIsInvisible(
enabled: Boolean = bugId == 0
) {
end("appPairsDividerIsInVisible", bugId, enabled) {
- this.notExists(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
+ this.notExists(APP_PAIR_SPLIT_DIVIDER)
}
}
@@ -107,7 +108,7 @@ fun LayersAssertionBuilder.appPairsPrimaryBoundsIsVisible(
enabled: Boolean = bugId == 0
) {
end("PrimaryAppBounds", bugId, enabled) {
- val dividerRegion = entry.getVisibleBounds(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
}
}
@@ -120,7 +121,7 @@ fun LayersAssertionBuilder.appPairsSecondaryBoundsIsVisible(
enabled: Boolean = bugId == 0
) {
end("SecondaryAppBounds", bugId, enabled) {
- val dividerRegion = entry.getVisibleBounds(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
index 3953c1c5a0f4..89bbdb0a2f99 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
@@ -135,12 +135,4 @@ abstract class FlickerTestBase {
throw RuntimeException(e)
}
}
-
- companion object {
- const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
- const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
- const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
- const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider"
- const val IMAGE_WALLPAPER = "ImageWallpaper"
- }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 5cbfec638da5..257350b6950b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -21,12 +21,12 @@ import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.FlickerTestBase.Companion.APP_PAIR_SPLIT_DIVIDER
import com.android.wm.shell.flicker.appPairsDividerIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index f57a000a0ccb..0b001f5ac1b6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -21,12 +21,12 @@ import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.FlickerTestBase.Companion.APP_PAIR_SPLIT_DIVIDER
import com.android.wm.shell.flicker.appPairsDividerIsInvisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt
deleted file mode 100644
index dea5c300f590..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt
+++ /dev/null
@@ -1,208 +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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.helpers.canSplitScreen
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isInSplitScreen
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.openQuickstep
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
-import org.junit.Assert
-import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test SplitScreen launch.
- * To run this test: `atest WMShellFlickerTests:EnterLegacySplitScreenTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterLegacySplitScreenTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
- private val splitScreenSetup: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- val testLaunchActivity = "launch_splitScreen_test_activity"
- withTestName {
- testLaunchActivity
- }
- setup {
- eachRun {
- uiDevice.wakeUpAndGoToHomeScreen()
- uiDevice.openQuickStepAndClearRecentAppsFromOverview()
- }
- }
- teardown {
- eachRun {
- if (uiDevice.isInSplitScreen()) {
- uiDevice.exitSplitScreen()
- }
- splitScreenApp.exit()
- secondaryApp.exit()
- nonResizeableApp.exit()
- }
- }
- assertions {
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- }
- }
-
- @Test
- fun testEnterSplitScreen_dockActivity() {
- val testTag = "testEnterSplitScreen_dockActivity"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- }
- assertions {
- layersTrace {
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- dockedStackDividerBecomesVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- LIVE_WALLPAPER_PACKAGE_NAME)
- )
- }
- windowManagerTrace {
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- @Test
- fun testEnterSplitScreen_launchToSide() {
- val testTag = "testEnterSplitScreen_launchToSide"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- secondaryApp.launchViaIntent()
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- splitScreenApp.reopenAppFromOverview()
- }
- assertions {
- layersTrace {
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- dockedStackSecondaryBoundsIsVisible(
- rotation, secondaryApp.defaultWindowName, 169271943)
- dockedStackDividerBecomesVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
- }
- windowManagerTrace {
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- .isVisible(secondaryApp.defaultWindowName)
- }
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName))
- }
- }
- }
- }
-
- @FlakyTest(bugId = 173875043)
- @Test
- fun testNonResizeableNotDocked() {
- val testTag = "testNonResizeableNotDocked"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- nonResizeableApp.launchViaIntent()
- uiDevice.openQuickstep()
- if (uiDevice.canSplitScreen()) {
- Assert.fail("Non-resizeable app should not enter split screen")
- }
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, nonResizeableApp.defaultWindowName)
- )
- }
- windowManagerTrace {
- end("appWindowIsVisible") {
- isInvisible(nonResizeableApp.defaultWindowName)
- }
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, nonResizeableApp.defaultWindowName))
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
new file mode 100644
index 000000000000..5374bd9f40de
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.wm.shell.flicker.legacysplitscreen
+
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.WALLPAPER_TITLE
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open activity and dock to primary split screen
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenDockActivity`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class EnterSplitScreenDockActivity(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testLegacySplitScreenDockActivity", configuration)
+ }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ device.launchSplitScreen()
+ }
+ assertions {
+ layersTrace {
+ dockedStackPrimaryBoundsIsVisible(
+ configuration.startRotation,
+ splitScreenApp.defaultWindowName, bugId = 169271943)
+ dockedStackDividerBecomesVisible()
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME,
+ WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
+ splitScreenApp.defaultWindowName),
+ bugId = 178531736
+ )
+ }
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME,
+ WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
+ splitScreenApp.defaultWindowName),
+ bugId = 178531736
+ )
+ end("appWindowIsVisible") {
+ isVisible(splitScreenApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, defaultTransitionSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
new file mode 100644
index 000000000000..d750403d66c6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.wm.shell.flicker.legacysplitscreen
+
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.appWindowBecomesVisible
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open activity to primary split screen and dock secondary activity to side
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenLaunchToSide`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class EnterSplitScreenLaunchToSide(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testLegacySplitScreenLaunchToSide", configuration)
+ }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ device.launchSplitScreen()
+ secondaryApp.reopenAppFromOverview()
+ }
+ assertions {
+ layersTrace {
+ dockedStackPrimaryBoundsIsVisible(
+ configuration.startRotation,
+ splitScreenApp.defaultWindowName, bugId = 169271943)
+ dockedStackSecondaryBoundsIsVisible(
+ configuration.startRotation,
+ secondaryApp.defaultWindowName, bugId = 169271943)
+ dockedStackDividerBecomesVisible()
+ // TODO(b/178447631) Remove Splash Screen from white list when flicker lib
+ // add a wait for splash screen be gone
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME,
+ splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName),
+ bugId = 178447631
+ )
+ }
+ windowManagerTrace {
+ appWindowBecomesVisible(secondaryApp.defaultWindowName)
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME,
+ splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName)
+ )
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, defaultTransitionSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
new file mode 100644
index 000000000000..e3619235ee77
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.wm.shell.flicker.legacysplitscreen
+
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.WALLPAPER_TITLE
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.canSplitScreen
+import com.android.server.wm.flicker.helpers.openQuickstep
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assert
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open non-resizable activity will auto exit split screen mode
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenNonResizableNotDock`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 173875043)
+class EnterSplitScreenNonResizableNotDock(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testLegacySplitScreenNonResizeableActivityNotDock", configuration)
+ }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ nonResizeableApp.launchViaIntent(wmHelper)
+ device.openQuickstep()
+ if (device.canSplitScreen()) {
+ Assert.fail("Non-resizeable app should not enter split screen")
+ }
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsInvisible()
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME,
+ SPLASH_SCREEN_NAME,
+ nonResizeableApp.defaultWindowName,
+ splitScreenApp.defaultWindowName),
+ bugId = 178447631
+ )
+ }
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(WALLPAPER_TITLE,
+ LAUNCHER_PACKAGE_NAME,
+ SPLASH_SCREEN_NAME,
+ nonResizeableApp.defaultWindowName,
+ splitScreenApp.defaultWindowName)
+ )
+ end("appWindowIsVisible") {
+ isInvisible(nonResizeableApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, defaultTransitionSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
new file mode 100644
index 000000000000..6aed83f35327
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.legacysplitscreen
+
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.appWindowBecomesInVisible
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.layerBecomesInvisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open resizeable activity split in primary, and drag divider to bottom exit split screen
+ * To run this test: `atest WMShellFlickerTests:ExitLegacySplitScreenFromBottom`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class ExitLegacySplitScreenFromBottom(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testExitLegacySplitScreenFromBottom", configuration)
+ }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ device.launchSplitScreen()
+ device.exitSplitScreenFromBottom()
+ }
+ assertions {
+ layersTrace {
+ layerBecomesInvisible(DOCKED_STACK_DIVIDER)
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName),
+ bugId = 178447631
+ )
+ }
+ windowManagerTrace {
+ appWindowBecomesInVisible(secondaryApp.defaultWindowName)
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName)
+ )
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, defaultTransitionSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt
deleted file mode 100644
index a3b8673d93ed..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt
+++ /dev/null
@@ -1,95 +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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom
-import com.android.server.wm.flicker.helpers.isInSplitScreen
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.repetitions
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:ExitLegacySplitScreenFromBottomTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExitLegacySplitScreenFromBottomTest(
- testSpec: FlickerTestRunnerFactory.TestSpec
-) : FlickerTestRunner(testSpec) {
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
- // TODO(b/162923992) Use of multiple segments of flicker spec for testing
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)) {
- configuration ->
- withTestName {
- buildTestTag("exitSplitScreenFromBottom", configuration)
- }
- repeat { configuration.repetitions }
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview()
- splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen()
- device.waitForIdle()
- this.setRotation(configuration.endRotation)
- }
- }
- teardown {
- eachRun {
- if (device.isInSplitScreen()) {
- device.exitSplitScreen()
- }
- splitScreenApp.exit()
- }
- }
- transitions {
- device.exitSplitScreenFromBottom()
- }
- assertions {
- windowManagerTrace {
- all("isNotEmpty") { isNotEmpty() }
- }
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt
deleted file mode 100644
index 701b0d05e65c..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt
+++ /dev/null
@@ -1,153 +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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.util.Rational
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.resizeSplitScreen
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test exit SplitScreen mode.
- * To run this test: `atest WMShellFlickerTests:ExitLegacySplitScreenTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExitLegacySplitScreenTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
- private val splitScreenSetup: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- val testLaunchActivity = "launch_splitScreen_test_activity"
- withTestName {
- testLaunchActivity
- }
- setup {
- eachRun {
- uiDevice.wakeUpAndGoToHomeScreen()
- uiDevice.openQuickStepAndClearRecentAppsFromOverview()
- secondaryApp.launchViaIntent()
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- }
- }
- teardown {
- eachRun {
- splitScreenApp.exit()
- secondaryApp.exit()
- }
- }
- assertions {
- windowManagerTrace {
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
- layersTrace {
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME))
- }
- }
- }
-
- @Test
- fun testEnterSplitScreen_exitPrimarySplitScreenMode() {
- val testTag = "testEnterSplitScreen_exitPrimarySplitScreenMode"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- uiDevice.exitSplitScreen()
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- layerBecomesInvisible(splitScreenApp.defaultWindowName)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsInvisible") {
- isInvisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- @Test
- @FlakyTest(bugId = 172811376)
- fun testEnterSplitScreen_exitPrimary_showSecondaryAppFullScreen() {
- val testTag = "testEnterSplitScreen_exitPrimary_showSecondaryAppFullScreen"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- splitScreenApp.reopenAppFromOverview()
- uiDevice.resizeSplitScreen(startRatio)
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- companion object {
- private val startRatio = Rational(1, 3)
-
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
new file mode 100644
index 000000000000..59f6aaf7dd6a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.wm.shell.flicker.legacysplitscreen
+
+import android.os.Bundle
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.appWindowBecomesInVisible
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.layerBecomesInvisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test dock activity to primary split screen, and open secondary to side, exit primary split
+ * and test secondary activity become full screen.
+ * To run this test: `atest WMShellFlickerTests:ExitPrimarySplitScreenShowSecondaryFullscreen`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class ExitPrimarySplitScreenShowSecondaryFullscreen(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testExitPrimarySplitScreenShowSecondaryFullscreen", configuration)
+ }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ device.launchSplitScreen()
+ secondaryApp.reopenAppFromOverview()
+ // TODO(b/175687842) Can not find Split screen divider, use exit() instead
+ splitScreenApp.exit()
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsInvisible(bugId = 175687842)
+ layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName),
+ bugId = 178447631
+ )
+ }
+ windowManagerTrace {
+ appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName),
+ bugId = 178447631
+ )
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, defaultTransitionSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncherTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 4dcbdfff8cd5..03b6edf0ff2a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncherTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -52,13 +52,13 @@ import org.junit.runners.Parameterized
/**
* Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:LegacySplitScreenToLauncherTest`
+ * To run this test: `atest WMShellFlickerTests:LegacySplitScreenToLauncher`
*/
@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class LegacySplitScreenToLauncherTest(
+class LegacySplitScreenToLauncher(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
companion object {
@@ -67,68 +67,68 @@ class LegacySplitScreenToLauncherTest(
fun getParams(): Collection<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation)
- .launcherStrategy.supportedLauncherPackage
+ .launcherStrategy.supportedLauncherPackage
val testApp = SimpleAppHelper(instrumentation)
// b/161435597 causes the test not to work on 90 degrees
return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
- withTestName {
- buildTestTag("splitScreenToLauncher", configuration)
+ withTestName {
+ buildTestTag("splitScreenToLauncher", configuration)
+ }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ device.openQuickStepAndClearRecentAppsFromOverview()
}
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview()
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.endRotation)
- device.launchSplitScreen()
- device.waitForIdle()
- }
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ this.setRotation(configuration.endRotation)
+ device.launchSplitScreen()
+ device.waitForIdle()
}
- teardown {
- eachRun {
- testApp.exit()
- }
- test {
- if (device.isInSplitScreen()) {
- device.exitSplitScreen()
- }
+ }
+ teardown {
+ eachRun {
+ testApp.exit()
+ }
+ test {
+ if (device.isInSplitScreen()) {
+ device.exitSplitScreen()
}
}
- transitions {
- device.exitSplitScreen()
+ }
+ transitions {
+ device.exitSplitScreen()
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
}
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.endRotation)
- navBarLayerRotatesAndScales(configuration.endRotation)
- statusBarLayerRotatesScales(configuration.endRotation)
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(launcherPackageName))
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.endRotation)
+ navBarLayerRotatesAndScales(configuration.endRotation)
+ statusBarLayerRotatesScales(configuration.endRotation)
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(launcherPackageName))
- // b/161435597 causes the test not to work on 90 degrees
- dockedStackDividerBecomesInvisible()
+ // b/161435597 causes the test not to work on 90 degrees
+ dockedStackDividerBecomesInvisible()
- layerBecomesInvisible(testApp.getPackage())
- }
+ layerBecomesInvisible(testApp.getPackage())
+ }
- eventLog {
- focusDoesNotChange(bugId = 151179149)
- }
+ eventLog {
+ focusDoesNotChange(bugId = 151179149)
}
}
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
new file mode 100644
index 000000000000..328ff88cd41b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -0,0 +1,99 @@
+/*
+ * 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/LICENSE2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.legacysplitscreen
+
+import android.app.Instrumentation
+import android.os.Bundle
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import android.view.Surface
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.startRotation
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+
+abstract class LegacySplitScreenTransition(
+ protected val instrumentation: Instrumentation
+) {
+ internal val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
+ internal val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
+ internal val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
+ internal val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
+ .launcherStrategy.supportedLauncherPackage
+ internal val LIVE_WALLPAPER_PACKAGE_NAME =
+ "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2"
+ internal val LETTERBOX_NAME = "Letterbox"
+ internal val TOAST_NAME = "Toast"
+ internal val SPLASH_SCREEN_NAME = "Splash Screen"
+
+ internal open val defaultTransitionSetup: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ setup {
+ eachRun {
+ device.wakeUpAndGoToHomeScreen()
+ device.openQuickStepAndClearRecentAppsFromOverview()
+ secondaryApp.launchViaIntent(wmHelper)
+ splitScreenApp.launchViaIntent(wmHelper)
+ this.setRotation(configuration.startRotation)
+ }
+ }
+ teardown {
+ eachRun {
+ splitScreenApp.exit()
+ secondaryApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ }
+
+ internal open val cleanSetup: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ setup {
+ eachRun {
+ device.wakeUpAndGoToHomeScreen()
+ device.openQuickStepAndClearRecentAppsFromOverview()
+ this.setRotation(configuration.startRotation)
+ }
+ }
+ teardown {
+ eachRun {
+ nonResizeableApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ }
+
+ internal open val customRotateSetup: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ setup {
+ eachRun {
+ device.wakeUpAndGoToHomeScreen()
+ device.openQuickStepAndClearRecentAppsFromOverview()
+ secondaryApp.launchViaIntent(wmHelper)
+ splitScreenApp.launchViaIntent(wmHelper)
+ }
+ }
+ teardown {
+ eachRun {
+ splitScreenApp.exit()
+ secondaryApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
new file mode 100644
index 000000000000..42c7b7c032cd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.wm.shell.flicker.legacysplitscreen
+
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.appWindowBecomesInVisible
+import com.android.server.wm.flicker.appWindowBecomesVisible
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.layerBecomesInvisible
+import com.android.server.wm.flicker.layerBecomesVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launch non resizable activity in split screen mode will trigger exit split screen mode
+ * (Non resizable activity launch via recent overview)
+ * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreen`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class NonResizableDismissInLegacySplitScreen(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testNonResizableDismissInLegacySplitScreen", configuration)
+ }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ nonResizeableApp.launchViaIntent(wmHelper)
+ splitScreenApp.launchViaIntent(wmHelper)
+ device.launchSplitScreen()
+ nonResizeableApp.reopenAppFromOverview()
+ wmHelper.waitForAppTransitionIdle()
+ }
+ assertions {
+ layersTrace {
+ layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME,
+ LETTERBOX_NAME, TOAST_NAME,
+ splitScreenApp.defaultWindowName,
+ nonResizeableApp.defaultWindowName),
+ bugId = 178447631
+ )
+ }
+ windowManagerTrace {
+ appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME,
+ LETTERBOX_NAME, TOAST_NAME,
+ splitScreenApp.defaultWindowName,
+ nonResizeableApp.defaultWindowName)
+ )
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, cleanSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
deleted file mode 100644
index 6fca5809b4fa..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
+++ /dev/null
@@ -1,92 +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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreenTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class NonResizableDismissInLegacySplitScreenTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
-
- @Test
- fun testNonResizableDismissInLegacySplitScreenTest() {
- val testTag = "testNonResizableDismissInLegacySplitScreenTest"
-
- runWithFlicker(transitionSetup) {
- withTestName { testTag }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- nonResizeableApp.launchViaIntent(wmHelper)
- splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen()
- nonResizeableApp.reopenAppFromOverview()
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- end("appsEndingBounds", enabled = false) {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds)
- }
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName, LETTER_BOX_NAME,
- TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME),
- bugId = 178447631
- )
- }
- windowManagerTrace {
- end("nonResizeableAppWindowIsVisible") {
- isVisible(nonResizeableApp.defaultWindowName)
- .isInvisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
new file mode 100644
index 000000000000..5b8ec1e9c61e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.wm.shell.flicker.legacysplitscreen
+
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.appWindowBecomesInVisible
+import com.android.server.wm.flicker.appWindowBecomesVisible
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.layerBecomesInvisible
+import com.android.server.wm.flicker.layerBecomesVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launch non resizable activity in split screen mode will trigger exit split screen mode
+ * (Non resizable activity launch via intent)
+ * To run this test: `atest WMShellFlickerTests:NonResizableLaunchInLegacySplitScreen`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class NonResizableLaunchInLegacySplitScreen(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testNonResizableLaunchInLegacySplitScreen", configuration)
+ }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ splitScreenApp.launchViaIntent(wmHelper)
+ device.launchSplitScreen()
+ nonResizeableApp.launchViaIntent(wmHelper)
+ wmHelper.waitForAppTransitionIdle()
+ }
+ assertions {
+ layersTrace {
+ layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(DOCKED_STACK_DIVIDER,
+ LAUNCHER_PACKAGE_NAME,
+ LETTERBOX_NAME,
+ nonResizeableApp.defaultWindowName,
+ splitScreenApp.defaultWindowName)
+ )
+ }
+ windowManagerTrace {
+ appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(DOCKED_STACK_DIVIDER,
+ LAUNCHER_PACKAGE_NAME,
+ LETTERBOX_NAME,
+ nonResizeableApp.defaultWindowName,
+ splitScreenApp.defaultWindowName)
+ )
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, cleanSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
deleted file mode 100644
index deae41fae0ca..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
+++ /dev/null
@@ -1,92 +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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:NonResizableLaunchInLegacySplitScreenTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class NonResizableLaunchInLegacySplitScreenTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
-
- @Test
- fun testNonResizableLaunchInLegacySplitScreenTest() {
- val testTag = "testNonResizableLaunchInLegacySplitScreenTest"
-
- runWithFlicker(transitionSetup) {
- withTestName { testTag }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- transitions {
- nonResizeableApp.launchViaIntent(wmHelper)
- splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen()
- nonResizeableApp.reopenAppFromOverview()
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- end("appsEndingBounds", enabled = false) {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds)
- }
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName, LETTER_BOX_NAME,
- TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME),
- bugId = 178447631
- )
- }
- windowManagerTrace {
- end("nonResizeableAppWindowIsVisible") {
- isVisible(nonResizeableApp.defaultWindowName)
- .isInvisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index 6200a69b795e..c802ffef204f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -16,94 +16,84 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.os.Bundle
import android.platform.test.annotations.Presubmit
-import android.view.Surface
import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.appWindowBecomesVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.layerBecomesVisible
+import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreenTest`
+ * To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreen`
*/
@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppToLegacySplitScreenTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
- @Test
- fun OpenAppToLegacySplitScreenTest() {
- val testTag = "OpenAppToLegacySplitScreenTest"
- val helper = WindowManagerStateHelper()
- runWithFlicker(transitionSetup) {
- withTestName { testTag }
- repeat { SplitScreenHelper.TEST_REPETITIONS }
- setup {
- eachRun {
- splitScreenApp.launchViaIntent(wmHelper)
- device.pressHome()
- this.setRotation(rotation)
+class OpenAppToLegacySplitScreen(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val wmHelper = WindowManagerStateHelper()
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testOpenAppToLegacySplitScreen", configuration)
}
- }
- transitions {
- device.launchSplitScreen()
- helper.waitForAppTransitionIdle()
- }
- assertions {
- windowManagerTrace {
- visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- LETTER_BOX_NAME)
- )
- appWindowBecomesVisible(splitScreenApp.getPackage())
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ device.launchSplitScreen()
+ wmHelper.waitForAppTransitionIdle()
}
+ assertions {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName),
+ bugId = 178447631)
+ appWindowBecomesVisible(splitScreenApp.getPackage())
+ }
- layersTrace {
- navBarLayerIsAlwaysVisible()
- noUncoveredRegions(rotation, enabled = false)
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- LETTER_BOX_NAME))
- appPairsDividerBecomesVisible()
- layerBecomesVisible(splitScreenApp.getPackage())
- }
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation, enabled = false)
+ statusBarLayerIsAlwaysVisible()
+ appPairsDividerBecomesVisible()
+ layerBecomesVisible(splitScreenApp.getPackage())
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName),
+ bugId = 178447631)
+ }
- eventLog {
- focusChanges(splitScreenApp.`package`,
+ eventLog {
+ focusChanges(splitScreenApp.`package`,
"recents_animation_input_consumer", "NexusLauncherActivity",
bugId = 151179149)
+ }
}
}
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- // TODO(b/161435597) causes the test not to work on 90 degrees
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, defaultTransitionSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 95c1c16385c4..54a37d71868d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -58,7 +58,7 @@ import org.junit.runners.Parameterized
/**
* Test split screen resizing window transitions.
- * To run this test: `atest WMShellFlickerTests:ResizeLegacySplitScreenTest`
+ * To run this test: `atest WMShellFlickerTests:ResizeLegacySplitScreen`
*
* Currently it runs only in 0 degrees because of b/156100803
*/
@@ -67,7 +67,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 159096424)
-class ResizeLegacySplitScreenTest(
+class ResizeLegacySplitScreen(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
new file mode 100644
index 000000000000..214269e13203
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.legacysplitscreen
+
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.appWindowBecomesVisible
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test dock activity to primary split screen and rotate
+ * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppAndEnterSplitScreen`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class RotateOneLaunchedAppAndEnterSplitScreen(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testRotateOneLaunchedAppAndEnterSplitScreen", configuration)
+ }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ device.launchSplitScreen()
+ this.setRotation(configuration.startRotation)
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsVisible(bugId = 175687842)
+ dockedStackPrimaryBoundsIsVisible(
+ configuration.startRotation,
+ splitScreenApp.defaultWindowName, bugId = 175687842)
+ navBarLayerRotatesAndScales(
+ configuration.startRotation,
+ configuration.endRotation, bugId = 169271943)
+ statusBarLayerRotatesScales(
+ configuration.startRotation,
+ configuration.endRotation, bugId = 169271943)
+ }
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, customRotateSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
new file mode 100644
index 000000000000..4290c923b38d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.legacysplitscreen
+
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.appWindowBecomesVisible
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Rotate
+ * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppInSplitScreenMode`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class RotateOneLaunchedAppInSplitScreenMode(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testRotateOneLaunchedAppInSplitScreenMode", configuration)
+ }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ this.setRotation(configuration.startRotation)
+ device.launchSplitScreen()
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsVisible(bugId = 175687842)
+ dockedStackPrimaryBoundsIsVisible(
+ configuration.startRotation,
+ splitScreenApp.defaultWindowName, bugId = 175687842)
+ navBarLayerRotatesAndScales(
+ configuration.startRotation,
+ configuration.endRotation, bugId = 169271943)
+ statusBarLayerRotatesScales(
+ configuration.startRotation,
+ configuration.endRotation, bugId = 169271943)
+ }
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, customRotateSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt
deleted file mode 100644
index 07571c3218a8..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt
+++ /dev/null
@@ -1,150 +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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isInSplitScreen
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class RotateOneLaunchedAppTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
- private val splitScreenRotationSetup: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- val testSetupRotation = "testSetupRotation"
- withTestName {
- testSetupRotation
- }
- setup {
- test {
- uiDevice.wakeUpAndGoToHomeScreen()
- uiDevice.openQuickStepAndClearRecentAppsFromOverview()
- }
- }
- teardown {
- eachRun {
- if (uiDevice.isInSplitScreen()) {
- uiDevice.exitSplitScreen()
- }
- setRotation(Surface.ROTATION_0)
- splitScreenApp.exit()
- secondaryApp.exit()
- }
- }
- }
-
- @Test
- fun testRotateInSplitScreenMode() {
- val testTag = "testEnterSplitScreen_launchToSide"
- runWithFlicker(splitScreenRotationSetup) {
- withTestName { testTag }
- repeat {
- SplitScreenHelper.TEST_REPETITIONS
- }
- transitions {
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- setRotation(rotation)
- }
- assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943)
- statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943)
- dockedStackDividerIsVisible()
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- @Test
- fun testRotateAndEnterSplitScreenMode() {
- val testTag = "testRotateAndEnterSplitScreenMode"
- runWithFlicker(splitScreenRotationSetup) {
- withTestName { testTag }
- repeat {
- SplitScreenHelper.TEST_REPETITIONS
- }
- transitions {
- splitScreenApp.launchViaIntent()
- setRotation(rotation)
- uiDevice.launchSplitScreen()
- }
- assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943)
- statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943)
- dockedStackDividerIsVisible()
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_90, Surface.ROTATION_270)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
new file mode 100644
index 000000000000..4095b9a2e61e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.legacysplitscreen
+
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.appWindowBecomesVisible
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open app to split screen.
+ * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppAndEnterSplitScreen`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class RotateTwoLaunchedAppAndEnterSplitScreen(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testRotateTwoLaunchedAppAndEnterSplitScreen", configuration)
+ }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ this.setRotation(configuration.startRotation)
+ device.launchSplitScreen()
+ secondaryApp.reopenAppFromOverview()
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsVisible(bugId = 175687842)
+ dockedStackPrimaryBoundsIsVisible(
+ configuration.startRotation,
+ splitScreenApp.defaultWindowName, 175687842)
+ dockedStackSecondaryBoundsIsVisible(
+ configuration.startRotation,
+ secondaryApp.defaultWindowName, bugId = 175687842)
+ navBarLayerRotatesAndScales(
+ configuration.startRotation,
+ configuration.endRotation, bugId = 169271943)
+ statusBarLayerRotatesScales(
+ configuration.startRotation,
+ configuration.endRotation, bugId = 169271943)
+ }
+ windowManagerTrace {
+ appWindowBecomesVisible(secondaryApp.defaultWindowName)
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, customRotateSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
new file mode 100644
index 000000000000..aebf6067615e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.legacysplitscreen
+
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.appWindowBecomesVisible
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open app to split screen.
+ * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppInSplitScreenMode`
+ */
+@Presubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class RotateTwoLaunchedAppInSplitScreenMode(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ withTestName {
+ buildTestTag("testRotateTwoLaunchedAppInSplitScreenMode", configuration)
+ }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ setup {
+ eachRun {
+ device.launchSplitScreen()
+ splitScreenApp.reopenAppFromOverview()
+ this.setRotation(configuration.startRotation)
+ }
+ }
+ transitions {
+ this.setRotation(configuration.startRotation)
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsVisible(bugId = 175687842)
+ dockedStackPrimaryBoundsIsVisible(
+ configuration.startRotation,
+ splitScreenApp.defaultWindowName, bugId = 175687842)
+ dockedStackSecondaryBoundsIsVisible(
+ configuration.startRotation,
+ secondaryApp.defaultWindowName, bugId = 175687842)
+ navBarLayerRotatesAndScales(
+ configuration.startRotation,
+ configuration.endRotation, bugId = 169271943)
+ statusBarLayerRotatesScales(
+ configuration.startRotation,
+ configuration.endRotation, bugId = 169271943)
+ }
+ windowManagerTrace {
+ appWindowBecomesVisible(secondaryApp.defaultWindowName)
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+ }
+ }
+ return FlickerTestRunnerFactory.getInstance().buildTest(
+ instrumentation, customRotateSetup, testSpec,
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */))
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt
deleted file mode 100644
index d8014d37dfad..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt
+++ /dev/null
@@ -1,163 +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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isInSplitScreen
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class RotateTwoLaunchedAppTest(
- rotationName: String,
- rotation: Int
-) : SplitScreenTestBase(rotationName, rotation) {
- private val splitScreenRotationSetup: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- val testSetupRotation = "testSetupRotation"
- withTestName {
- testSetupRotation
- }
- setup {
- test {
- uiDevice.wakeUpAndGoToHomeScreen()
- uiDevice.openQuickStepAndClearRecentAppsFromOverview()
- }
- }
- teardown {
- eachRun {
- if (uiDevice.isInSplitScreen()) {
- uiDevice.exitSplitScreen()
- }
- setRotation(Surface.ROTATION_0)
- splitScreenApp.exit()
- secondaryApp.exit()
- }
- }
- }
-
- @Test
- fun testRotateInSplitScreenMode() {
- val testTag = "testRotateInSplitScreenMode"
- runWithFlicker(splitScreenRotationSetup) {
- withTestName { testTag }
- repeat {
- SplitScreenHelper.TEST_REPETITIONS
- }
- transitions {
- secondaryApp.launchViaIntent()
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- splitScreenApp.reopenAppFromOverview()
- setRotation(rotation)
- }
- assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943)
- statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943)
- dockedStackDividerIsVisible()
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- dockedStackSecondaryBoundsIsVisible(
- rotation, secondaryApp.defaultWindowName, 169271943)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- .isVisible(secondaryApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- @FlakyTest(bugId = 173875043)
- @Test
- fun testRotateAndEnterSplitScreenMode() {
- val testTag = "testRotateAndEnterSplitScreenMode"
- runWithFlicker(splitScreenRotationSetup) {
- withTestName { testTag }
- repeat {
- SplitScreenHelper.TEST_REPETITIONS
- }
- transitions {
- secondaryApp.launchViaIntent()
- splitScreenApp.launchViaIntent()
- setRotation(rotation)
- uiDevice.launchSplitScreen()
- splitScreenApp.reopenAppFromOverview()
- }
- assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943)
- statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943)
- dockedStackDividerIsVisible()
- dockedStackPrimaryBoundsIsVisible(
- rotation, splitScreenApp.defaultWindowName, 169271943)
- dockedStackSecondaryBoundsIsVisible(
- rotation, secondaryApp.defaultWindowName, 169271943)
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("appWindowIsVisible") {
- isVisible(splitScreenApp.defaultWindowName)
- .isVisible(secondaryApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_90, Surface.ROTATION_270)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt
deleted file mode 100644
index 01db4ed6253e..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt
+++ /dev/null
@@ -1,74 +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.wm.shell.flicker.legacysplitscreen
-
-import android.support.test.launcherhelper.LauncherStrategyFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isInSplitScreen
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.NonRotationTestBase
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-
-abstract class SplitScreenTestBase(
- rotationName: String,
- rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
- protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
- protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
- protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
- protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
- .launcherStrategy.supportedLauncherPackage
- protected val LIVE_WALLPAPER_PACKAGE_NAME =
- "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2"
- protected val LETTER_BOX_NAME = "Letterbox"
- protected val TOAST_NAME = "Toast"
-
- protected val transitionSetup: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- uiDevice.wakeUpAndGoToHomeScreen()
- uiDevice.openQuickStepAndClearRecentAppsFromOverview()
- }
- }
- teardown {
- eachRun {
- if (uiDevice.isInSplitScreen()) {
- uiDevice.exitSplitScreen()
- }
- splitScreenApp.exit()
- nonResizeableApp.exit()
- }
- }
- assertions {
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- }
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- }
- }
-}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 615bf4de9fcc..ce1d96c167d7 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -435,6 +435,7 @@ cc_defaults {
"canvas/CanvasFrontend.cpp",
"canvas/CanvasOpBuffer.cpp",
"canvas/CanvasOpRasterizer.cpp",
+ "effects/StretchEffect.cpp",
"pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaRecordingCanvas.cpp",
"pipeline/skia/RenderNodeDrawable.cpp",
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 96118aaec29b..64b8b711f0a8 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -561,7 +561,7 @@ public:
return;
}
c->concat(invertedMatrix);
- mLayerSurface->draw(c, deviceBounds.fLeft, deviceBounds.fTop, nullptr);
+ mLayerSurface->draw(c, deviceBounds.fLeft, deviceBounds.fTop);
} else {
c->drawDrawable(drawable.get());
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index aeb60e6ce355..609706e2e49d 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -23,6 +23,7 @@
#include "Outline.h"
#include "Rect.h"
#include "RevealClip.h"
+#include "effects/StretchEffect.h"
#include "utils/MathUtils.h"
#include "utils/PaintUtils.h"
@@ -98,6 +99,10 @@ public:
SkImageFilter* getImageFilter() const { return mImageFilter.get(); }
+ const StretchEffect& getStretchEffect() const { return mStretchEffect; }
+
+ StretchEffect& mutableStretchEffect() { return mStretchEffect; }
+
// Sets alpha, xfermode, and colorfilter from an SkPaint
// paint may be NULL, in which case defaults will be set
bool setFromPaint(const SkPaint* paint);
@@ -124,6 +129,7 @@ private:
SkBlendMode mMode;
sk_sp<SkColorFilter> mColorFilter;
sk_sp<SkImageFilter> mImageFilter;
+ StretchEffect mStretchEffect;
};
/*
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
new file mode 100644
index 000000000000..51cbc7592861
--- /dev/null
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "StretchEffect.h"
+
+namespace android::uirenderer {
+
+sk_sp<SkImageFilter> StretchEffect::getImageFilter() const {
+ // TODO: Implement & Cache
+ // Probably need to use mutable to achieve caching
+ return nullptr;
+}
+
+} // namespace android::uirenderer \ No newline at end of file
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
new file mode 100644
index 000000000000..7dfd6398765a
--- /dev/null
+++ b/libs/hwui/effects/StretchEffect.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "utils/MathUtils.h"
+
+#include <SkPoint.h>
+#include <SkRect.h>
+#include <SkImageFilter.h>
+
+namespace android::uirenderer {
+
+// TODO: Inherit from base RenderEffect type?
+class StretchEffect {
+public:
+ enum class StretchInterpolator {
+ SmoothStep,
+ };
+
+ bool isEmpty() const {
+ return MathUtils::isZero(stretchDirection.x())
+ && MathUtils::isZero(stretchDirection.y());
+ }
+
+ void setEmpty() {
+ *this = StretchEffect{};
+ }
+
+ void mergeWith(const StretchEffect& other) {
+ if (other.isEmpty()) {
+ return;
+ }
+ if (isEmpty()) {
+ *this = other;
+ return;
+ }
+ stretchDirection += other.stretchDirection;
+ if (isEmpty()) {
+ return setEmpty();
+ }
+ stretchArea.join(other.stretchArea);
+ maxStretchAmount = std::max(maxStretchAmount, other.maxStretchAmount);
+ }
+
+ sk_sp<SkImageFilter> getImageFilter() const;
+
+ SkRect stretchArea {0, 0, 0, 0};
+ SkVector stretchDirection {0, 0};
+ float maxStretchAmount = 0;
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 8b35d96aeac8..80239687a7fb 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -166,6 +166,31 @@ static jboolean android_view_RenderNode_setOutlineNone(CRITICAL_JNI_PARAMS_COMMA
return true;
}
+static jboolean android_view_RenderNode_clearStretch(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ auto& stretch = renderNode->mutateStagingProperties()
+ .mutateLayerProperties().mutableStretchEffect();
+ if (stretch.isEmpty()) {
+ return false;
+ }
+ stretch.setEmpty();
+ renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return true;
+}
+
+static jboolean android_view_RenderNode_stretch(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+ jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat vX, jfloat vY, jfloat max) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ renderNode->mutateStagingProperties().mutateLayerProperties().mutableStretchEffect().mergeWith(
+ StretchEffect{
+ .stretchArea = SkRect::MakeLTRB(left, top, right, bottom),
+ .stretchDirection = {.fX = vX, .fY = vY},
+ .maxStretchAmount = max
+ });
+ renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return true;
+}
+
static jboolean android_view_RenderNode_hasShadow(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().hasShadow();
@@ -678,6 +703,8 @@ static const JNINativeMethod gMethods[] = {
{ "nSetOutlinePath", "(JJF)Z", (void*) android_view_RenderNode_setOutlinePath },
{ "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty },
{ "nSetOutlineNone", "(J)Z", (void*) android_view_RenderNode_setOutlineNone },
+ { "nClearStretch", "(J)Z", (void*) android_view_RenderNode_clearStretch },
+ { "nStretch", "(JFFFFFFF)Z", (void*) android_view_RenderNode_stretch },
{ "nHasShadow", "(J)Z", (void*) android_view_RenderNode_hasShadow },
{ "nSetSpotShadowColor", "(JI)Z", (void*) android_view_RenderNode_setSpotShadowColor },
{ "nGetSpotShadowColor", "(J)I", (void*) android_view_RenderNode_getSpotShadowColor },
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index c6c9e9dc869a..71f533c3fc4f 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -194,7 +194,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
canvas->concat(invertedMatrix);
const SkIRect deviceBounds = canvas->getDeviceClipBounds();
- tmpSurface->draw(canvas, deviceBounds.fLeft, deviceBounds.fTop, nullptr);
+ tmpSurface->draw(canvas, deviceBounds.fLeft, deviceBounds.fTop);
}
}
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 75815bb6e63d..c01021221f37 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -20,6 +20,8 @@
#include "SkiaDisplayList.h"
#include "utils/TraceUtils.h"
+#include <include/effects/SkImageFilters.h>
+
#include <optional>
namespace android {
@@ -171,11 +173,25 @@ static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultip
SkPaint* paint) {
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
- properties.getImageFilter() != nullptr) {
+ properties.getImageFilter() != nullptr || !properties.getStretchEffect().isEmpty()) {
paint->setAlpha(properties.alpha() * alphaMultiplier);
paint->setBlendMode(properties.xferMode());
paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
- paint->setImageFilter(sk_ref_sp(properties.getImageFilter()));
+
+ sk_sp<SkImageFilter> imageFilter = sk_ref_sp(properties.getImageFilter());
+ sk_sp<SkImageFilter> stretchFilter = properties.getStretchEffect().getImageFilter();
+ sk_sp<SkImageFilter> filter;
+ if (imageFilter && stretchFilter) {
+ filter = SkImageFilters::Compose(
+ std::move(stretchFilter),
+ std::move(imageFilter)
+ );
+ } else if (stretchFilter) {
+ filter = std::move(stretchFilter);
+ } else {
+ filter = std::move(imageFilter);
+ }
+ paint->setImageFilter(std::move(filter));
return true;
}
return false;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index eacabfd1dbf9..633f21ceba07 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -490,9 +490,11 @@ void CanvasContext::draw() {
if (mNativeSurface) {
// TODO(b/165985262): measure performance impact
- if (const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
- vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
- native_window_set_frame_timeline_vsync(mNativeSurface->getNativeWindow(), vsyncId);
+ const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+ if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
+ const auto inputEventId = mCurrentFrameInfo->get(FrameInfoIndex::NewestInputEvent);
+ native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), vsyncId,
+ inputEventId);
}
}
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 3d188c0acce3..65a0110ad87e 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -31,6 +31,7 @@ import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
@@ -1775,6 +1776,7 @@ public final class GnssMeasurement implements Parcelable {
*/
@Nullable
@SystemApi
+ @SuppressLint("NullableCollection")
public Collection<CorrelationVector> getCorrelationVectors() {
return mReadOnlyCorrelationVectors;
}
@@ -1785,7 +1787,9 @@ public final class GnssMeasurement implements Parcelable {
* @hide
*/
@TestApi
- public void setCorrelationVectors(@Nullable Collection<CorrelationVector> correlationVectors) {
+ public void setCorrelationVectors(
+ @SuppressLint("NullableCollection")
+ @Nullable Collection<CorrelationVector> correlationVectors) {
if (correlationVectors == null || correlationVectors.isEmpty()) {
resetCorrelationVectors();
} else {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 96ec59021d28..fdb044d0dcf6 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2233,6 +2233,7 @@ public class LocationManager {
* @see #getGnssCapabilities()
*/
@Nullable
+ @SuppressLint("NullableCollection")
public List<GnssAntennaInfo> getGnssAntennaInfos() {
try {
return mService.getGnssAntennaInfos();
diff --git a/location/java/android/location/provider/LocationProviderBase.java b/location/java/android/location/provider/LocationProviderBase.java
index 1306ea2c0204..8f455cd7df07 100644
--- a/location/java/android/location/provider/LocationProviderBase.java
+++ b/location/java/android/location/provider/LocationProviderBase.java
@@ -255,7 +255,9 @@ public abstract class LocationProviderBase {
/**
* Implements optional custom commands.
*/
- public abstract void onSendExtraCommand(@NonNull String command, @Nullable Bundle extras);
+ public abstract void onSendExtraCommand(@NonNull String command,
+ @SuppressLint("NullableCollection")
+ @Nullable Bundle extras);
private final class Service extends ILocationProvider.Stub {
diff --git a/media/java/android/media/musicrecognition/MusicRecognitionManager.java b/media/java/android/media/musicrecognition/MusicRecognitionManager.java
index 6bbcfd3b5b66..b183eaff83ec 100644
--- a/media/java/android/media/musicrecognition/MusicRecognitionManager.java
+++ b/media/java/android/media/musicrecognition/MusicRecognitionManager.java
@@ -23,6 +23,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
@@ -90,7 +91,9 @@ public class MusicRecognitionManager {
* supplied bundle
*/
void onRecognitionSucceeded(@NonNull RecognitionRequest recognitionRequest,
- @NonNull MediaMetadata result, @Nullable Bundle extras);
+ @NonNull MediaMetadata result,
+ @SuppressLint("NullableCollection")
+ @Nullable Bundle extras);
/**
* Invoked when the search is not successful (possibly but not necessarily due to error).
diff --git a/media/java/android/media/musicrecognition/MusicRecognitionService.java b/media/java/android/media/musicrecognition/MusicRecognitionService.java
index e2071b84c00b..04b4c39bf0fa 100644
--- a/media/java/android/media/musicrecognition/MusicRecognitionService.java
+++ b/media/java/android/media/musicrecognition/MusicRecognitionService.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
@@ -53,7 +54,9 @@ public abstract class MusicRecognitionService extends Service {
* @param extras extra data to be supplied back to the caller. Note that all executable
* parameters and file descriptors would be removed from the supplied bundle
*/
- void onRecognitionSucceeded(@NonNull MediaMetadata result, @Nullable Bundle extras);
+ void onRecognitionSucceeded(@NonNull MediaMetadata result,
+ @SuppressLint("NullableCollection")
+ @Nullable Bundle extras);
/**
* Call this method if the search does not find a result on an error occurred.
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 4d835b22e34c..8525e9979aef 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -22,6 +22,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
@@ -1017,6 +1018,7 @@ public class Tuner implements AutoCloseable {
* failed.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public List<FrontendInfo> getAvailableFrontendInfos() {
FrontendInfo[] feInfoList = getFrontendInfoListInternal();
if (feInfoList == null) {
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 9f327c93f63b..51b685aeb8be 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -284,6 +284,12 @@ public class Filter implements AutoCloseable {
/**
* Configures the filter.
*
+ * <p>Recofiguring must happen after stopping the filter.
+ *
+ * <p>When stopping, reconfiguring and restarting the filter, the client should discard all
+ * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
+ * using the events from the previous configuration.
+ *
* @param config the configuration of the filter.
* @return result status of the operation.
*/
@@ -393,6 +399,10 @@ public class Filter implements AutoCloseable {
*
* <p>Does nothing if the filter is already started.
*
+ * <p>When stopping, reconfiguring and restarting the filter, the client should discard all
+ * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
+ * using the events from the previous configuration.
+ *
* @return result status of the operation.
*/
@Result
@@ -409,6 +419,12 @@ public class Filter implements AutoCloseable {
*
* <p>Does nothing if the filter is stopped or not started.
*
+ * <p>Filter must be stopped to reconfigure.
+ *
+ * <p>When stopping, reconfiguring and restarting the filter, the client should discard all
+ * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
+ * using the events from the previous configuration.
+ *
* @return result status of the operation.
*/
@Result
diff --git a/media/java/android/media/tv/tuner/filter/RestartEvent.java b/media/java/android/media/tv/tuner/filter/RestartEvent.java
index 069630188e55..9c5992af5f48 100644
--- a/media/java/android/media/tv/tuner/filter/RestartEvent.java
+++ b/media/java/android/media/tv/tuner/filter/RestartEvent.java
@@ -19,17 +19,29 @@ package android.media.tv.tuner.filter;
import android.annotation.SystemApi;
/**
- * An Event that the client would reveice after stopping, reconfiguring and restarting a filter.
+ * An Event that the client would receive after starting a filter. This event is optional to be
+ * received on the newly opened and started filter. It must be received after stopping,
+ * reconfiguring and restarting a Filter to differentiate the valid reconfigured events from the
+ * previous events.
*
* <p>After stopping and restarting the filter, the client has to discard all coming events until
* it receives {@link RestartEvent} to avoid using the events from the previous configuration.
*
* <p>Recofiguring must happen after stopping the filter.
*
+ * @see Filter#stop()
+ * @see Filter#start()
+ * @see Filter#configure(FilterConfiguration)
+ *
* @hide
*/
@SystemApi
public final class RestartEvent extends FilterEvent {
+ /**
+ * The stard id reserved for the newly opened filter's first start event.
+ */
+ public static final int NEW_FILTER_FIRST_START_ID = 0;
+
private final int mStartId;
// This constructor is used by JNI code only
@@ -38,13 +50,13 @@ public final class RestartEvent extends FilterEvent {
}
/**
- * Gets the start id.
+ * Gets the start id sent via the current Restart Event.
*
- * <p>An unique ID to mark the start point of receiving the valid filter events after
- * reconfiguring. It must be sent at least once in the first event after the filter is
- * restarted.
+ * <p>An unique ID to mark the start point of receiving the valid reconfigured filter events.
+ * The client must receive at least once after the filter is reconfigured and restarted.
*
- * <p>0 is reserved for the newly opened filter's first start. It's optional to be received.
+ * <p>{@link #NEW_FILTER_FIRST_START_ID} is reserved for the newly opened filter's first start.
+ * It's optional to be received.
*/
public int getStartId() {
return mStartId;
diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp
index c064de299b19..08a8d897c96e 100644
--- a/media/jni/android_media_MediaMetricsJNI.cpp
+++ b/media/jni/android_media_MediaMetricsJNI.cpp
@@ -18,7 +18,6 @@
#include <binder/Parcel.h>
#include <jni.h>
-#include <media/IMediaMetricsService.h>
#include <media/MediaMetricsItem.h>
#include <nativehelper/JNIHelp.h>
#include <variant>
@@ -151,12 +150,7 @@ static jint android_media_MediaMetrics_submit_bytebuffer(
return (jint)BAD_VALUE;
}
- sp<IMediaMetricsService> service = mediametrics::BaseItem::getService();
- if (service == nullptr) {
- ALOGW("Cannot retrieve mediametrics service");
- return (jint)NO_INIT;
- }
- return (jint)service->submitBuffer((char *)buffer, length);
+ return (jint)mediametrics::BaseItem::submitBuffer((char *)buffer, length);
}
// Helper function to convert a native PersistableBundle to a Java
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index d464587cb6c8..3d633ea7089d 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -27,14 +27,13 @@ cc_library_shared {
],
shared_libs: [
- "libandroid_runtime",
"libhwui",
"liblog",
],
header_libs: [
- "libhwui_internal_headers",
"jni_headers",
+ "libhwui_internal_headers",
],
static_libs: ["libarect"],
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
new file mode 100644
index 000000000000..9afa5d1311c5
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+
+/**
+ * Class for performing registration for all core connectivity services.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ConnectivityFrameworkInitializer {
+ private ConnectivityFrameworkInitializer() {}
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers all core
+ * connectivity services to {@link Context}, so that {@link Context#getSystemService} can
+ * return them.
+ *
+ * @throws IllegalStateException if this is called anywhere besides
+ * {@link SystemServiceRegistry}.
+ */
+ public static void registerServiceWrappers() {
+ // registerContextAwareService will throw if this is called outside of SystemServiceRegistry
+ // initialization.
+ SystemServiceRegistry.registerContextAwareService(
+ Context.CONNECTIVITY_SERVICE,
+ ConnectivityManager.class,
+ (context, serviceBinder) -> {
+ IConnectivityManager icm = IConnectivityManager.Stub.asInterface(serviceBinder);
+ return new ConnectivityManager(context, icm);
+ }
+ );
+
+ // TODO: move outside of the connectivity JAR
+ SystemServiceRegistry.registerContextAwareService(
+ Context.VPN_MANAGEMENT_SERVICE,
+ VpnManager.class,
+ (context) -> {
+ final ConnectivityManager cm = context.getSystemService(
+ ConnectivityManager.class);
+ return cm.createVpnManager();
+ }
+ );
+
+ SystemServiceRegistry.registerContextAwareService(
+ Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+ ConnectivityDiagnosticsManager.class,
+ (context) -> {
+ final ConnectivityManager cm = context.getSystemService(
+ ConnectivityManager.class);
+ return cm.createDiagnosticsManager();
+ }
+ );
+
+ SystemServiceRegistry.registerContextAwareService(
+ Context.TEST_NETWORK_SERVICE,
+ TestNetworkManager.class,
+ context -> {
+ final ConnectivityManager cm = context.getSystemService(
+ ConnectivityManager.class);
+ return cm.startOrGetTestNetworkManager();
+ }
+ );
+ }
+}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index ac8f9c9e62fd..2a0a74ed0604 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -4823,6 +4823,28 @@ public class ConnectivityManager {
}
}
+ /** @hide */
+ public TestNetworkManager startOrGetTestNetworkManager() {
+ final IBinder tnBinder;
+ try {
+ tnBinder = mService.startOrGetTestNetworkService();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ return new TestNetworkManager(ITestNetworkManager.Stub.asInterface(tnBinder));
+ }
+
+ /** @hide */
+ public VpnManager createVpnManager() {
+ return new VpnManager(mContext, mService);
+ }
+
+ /** @hide */
+ public ConnectivityDiagnosticsManager createDiagnosticsManager() {
+ return new ConnectivityDiagnosticsManager(mContext, mService);
+ }
+
/**
* Simulates a Data Stall for the specified Network.
*
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 3843b9ab93c2..8bfa77acd36d 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -34,9 +34,9 @@ import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
import com.android.internal.util.Preconditions;
+import com.android.net.module.util.CollectionUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -401,11 +401,18 @@ public final class NetworkCapabilities implements Parcelable {
public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27;
/**
- * Indicates that this network is not managed by a Virtual Carrier Network (VCN).
- *
- * TODO(b/177299683): Add additional clarifying javadoc.
+ * Indicates that this network is not subsumed by a Virtual Carrier Network (VCN).
+ * <p>
+ * To provide an experience on a VCN similar to a single traditional carrier network, in
+ * some cases the system sets this bit is set by default in application's network requests,
+ * and may choose to remove it at its own discretion when matching the request to a network.
+ * <p>
+ * Applications that want to know about a Virtual Carrier Network's underlying networks,
+ * for example to use them for multipath purposes, should remove this bit from their network
+ * requests ; the system will not add it back once removed.
* @hide
*/
+ @SystemApi
public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
@@ -767,7 +774,7 @@ public final class NetworkCapabilities implements Parcelable {
if (originalOwnerUid == creatorUid) {
setOwnerUid(creatorUid);
}
- if (ArrayUtils.contains(originalAdministratorUids, creatorUid)) {
+ if (CollectionUtils.contains(originalAdministratorUids, creatorUid)) {
setAdministratorUids(new int[] {creatorUid});
}
// There is no need to clear the UIDs, they have already been cleared by clearAll() above.
@@ -1873,7 +1880,7 @@ public final class NetworkCapabilities implements Parcelable {
sb.append(" OwnerUid: ").append(mOwnerUid);
}
- if (!ArrayUtils.isEmpty(mAdministratorUids)) {
+ if (mAdministratorUids != null && mAdministratorUids.length != 0) {
sb.append(" AdminUids: ").append(Arrays.toString(mAdministratorUids));
}
@@ -2506,7 +2513,7 @@ public final class NetworkCapabilities implements Parcelable {
@NonNull
public NetworkCapabilities build() {
if (mCaps.getOwnerUid() != Process.INVALID_UID) {
- if (!ArrayUtils.contains(mCaps.getAdministratorUids(), mCaps.getOwnerUid())) {
+ if (!CollectionUtils.contains(mCaps.getAdministratorUids(), mCaps.getOwnerUid())) {
throw new IllegalStateException("The owner UID must be included in "
+ " administrator UIDs.");
}
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index c8f3bd3666e4..8fc318180778 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -57,6 +57,7 @@ java_library {
static_libs: [
"net-utils-device-common",
"net-utils-framework-common",
+ "netd-client",
],
apex_available: [
"//apex_available:platform",
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_off.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_off.xml
index 8a73fb52de75..2be00b95af3c 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_off.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_off.xml
@@ -15,11 +15,17 @@
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval" >
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="4dp"
+ android:left="4dp"
+ android:right="4dp"
+ android:bottom="4dp">
- <size android:height="24dp" android:width="24dp" />
- <solid android:color="@color/thumb_off" />
- <stroke android:width="4dp" android:color="@android:color/transparent" />
+ <shape android:shape="oval" >
+ <size android:height="20dp" android:width="20dp" />
+ <solid android:color="@color/thumb_off" />
+ </shape>
-</shape>
+ </item>
+</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_on.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_on.xml
index 8a0af00b0d61..e85eb42007b8 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_on.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/thumb_on.xml
@@ -15,11 +15,17 @@
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval" >
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="4dp"
+ android:left="4dp"
+ android:right="4dp"
+ android:bottom="4dp">
- <size android:height="24dp" android:width="24dp" />
- <solid android:color="?android:attr/colorAccent" />
- <stroke android:width="4dp" android:color="@android:color/transparent" />
+ <shape android:shape="oval" >
+ <size android:height="20dp" android:width="20dp" />
+ <solid android:color="?android:attr/colorAccent" />
+ </shape>
-</shape>
+ </item>
+</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off.xml
index 1be3a8eda19e..b29f4596d77c 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off.xml
@@ -20,7 +20,7 @@
<item
android:width="13.33dp"
android:height="1.67dp"
- android:left="19dp"
+ android:left="21dp"
android:gravity="center"
android:drawable="@drawable/track_off_indicator" />
</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off_background.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off_background.xml
index 3cc490f9815c..c838654dd209 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off_background.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_off_background.xml
@@ -17,17 +17,17 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="24dp"
- android:viewportWidth="48"
- android:viewportHeight="24">
+ android:width="52dp"
+ android:height="28dp"
+ android:viewportWidth="52"
+ android:viewportHeight="28">
<group>
<clip-path
- android:pathData="M12 0H36C42.6274 0 48 5.37258 48 12C48 18.6274 42.6274 24 36 24H12C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0Z" />
+ android:pathData="M14 0H38C45.732 0 52 6.26801 52 14C52 21.732 45.732 28 38 28H14C6.26801 28 0 21.732 0 14C0 6.26801 6.26801 0 14 0Z" />
<path
- android:pathData="M0 0V24H48V0"
+ android:pathData="M0 0V28H52V0"
android:fillColor="@color/track_off" />
</group>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on.xml
index 255389143782..cf241129f16d 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on.xml
@@ -21,6 +21,6 @@
android:width="13.19dp"
android:height="10.06dp"
android:gravity="center"
- android:right="19dp"
+ android:right="21dp"
android:drawable="@drawable/track_on_indicator" />
</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on_background.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on_background.xml
index 68ce19b6c67f..bb1a7ef9c733 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on_background.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable/track_on_background.xml
@@ -17,19 +17,20 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="24dp"
- android:viewportWidth="48"
- android:viewportHeight="24"
+ android:width="52dp"
+ android:height="28dp"
+ android:viewportWidth="52"
+ android:viewportHeight="28"
android:tint="@*android:color/switch_track_material">
<group>
<clip-path
- android:pathData="M12 0H36C42.6274 0 48 5.37258 48 12C48 18.6274 42.6274 24 36 24H12C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0Z" />
+ android:pathData="M14 0H38C45.732 0 52 6.26801 52 14C52 21.732 45.732 28 38 28H14C6.26801 28 0 21.732 0 14C0 6.26801 6.26801 0 14 0Z" />
<path
- android:pathData="M0 0V24H48V0"
+ android:pathData="M0 0V28H52V0"
android:fillColor="@*android:color/white_disabled_material" />
+
</group>
</vector>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
index 7e3ce9d3595c..52779bcabf00 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
@@ -26,7 +26,7 @@
android:minHeight="@dimen/min_switch_bar_height"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:background="?android:attr/colorBackground"
+ android:background="?android:attr/selectableItemBackground"
android:paddingLeft="@dimen/switchbar_margin_start"
android:paddingRight="@dimen/switchbar_margin_end">
@@ -35,7 +35,7 @@
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
- android:paddingRight="54dp"
+ android:layout_marginRight="16dp"
android:layout_gravity="center_vertical"
android:maxLines="2"
android:ellipsize="end"
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml b/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml
index 9dc0af3b5643..147db773fd05 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values-night/colors.xml
@@ -17,7 +17,6 @@
<resources>
- <color name="title_text_color">@*android:color/primary_text_dark</color>
<color name="thumb_off">#BFFFFFFF</color>
<color name="track_off">@*android:color/material_grey_600</color>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
index 8194bddc99b4..147db773fd05 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
@@ -17,7 +17,6 @@
<resources>
- <color name="title_text_color">@*android:color/primary_text_light</color>
<color name="thumb_off">#BFFFFFFF</color>
<color name="track_off">@*android:color/material_grey_600</color>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
index dd443deeb869..b145c9bc4b39 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
@@ -18,13 +18,13 @@
<resources>
<!-- Size of layout margin left -->
- <dimen name="switchbar_margin_start">26dp</dimen>
+ <dimen name="switchbar_margin_start">22dp</dimen>
<!-- Size of layout margin right -->
- <dimen name="switchbar_margin_end">24dp</dimen>
+ <dimen name="switchbar_margin_end">16dp</dimen>
<!-- Minimum width of switch -->
- <dimen name="min_switch_width">48dp</dimen>
+ <dimen name="min_switch_width">52dp</dimen>
<!-- Minimum width of switch bar -->
<dimen name="min_switch_bar_height">72dp</dimen>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
index fbb896c76295..59b5899a4173 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
@@ -19,7 +19,6 @@
<style name="MainSwitchText">
<item name="android:textSize">20sp</item>
- <item name="android:textColor">@color/title_text_color</item>
</style>
<style name="Settings.MainSwitch" parent="@android:style/Widget.Material.CompoundButton.Switch">
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 532c996824d4..74b65780ffc2 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -136,10 +136,8 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec
* Show the MainSwitchBar
*/
public void show() {
- if (!isShowing()) {
- setVisibility(View.VISIBLE);
- mSwitch.setOnCheckedChangeListener(this);
- }
+ setVisibility(View.VISIBLE);
+ mSwitch.setOnCheckedChangeListener(this);
}
/**
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
index dae0e705794d..274bf8df2222 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -17,9 +17,11 @@
package com.android.settingslib.widget;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.text.TextUtils;
import android.util.AttributeSet;
-import android.widget.Switch;
+import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.TwoStatePreference;
@@ -38,30 +40,29 @@ public class MainSwitchPreference extends TwoStatePreference {
private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>();
private MainSwitchBar mMainSwitchBar;
- private Switch mSwitch;
private String mTitle;
private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin;
public MainSwitchPreference(Context context) {
super(context);
- init();
+ init(context, null);
}
public MainSwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- init();
+ init(context, attrs);
}
public MainSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- init();
+ init(context, attrs);
}
public MainSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- init();
+ init(context, attrs);
}
@Override
@@ -76,8 +77,21 @@ public class MainSwitchPreference extends TwoStatePreference {
registerListenerToSwitchBar();
}
- private void init() {
+ private void init(Context context, AttributeSet attrs) {
setLayoutResource(R.layout.main_switch_layout);
+
+ if (attrs != null) {
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/,
+ 0 /*defStyleRes*/);
+ final CharSequence title = TypedArrayUtils.getText(a,
+ androidx.preference.R.styleable.Preference_title,
+ androidx.preference.R.styleable.Preference_android_title);
+ if (!TextUtils.isEmpty(title)) {
+ setTitle(title.toString());
+ }
+ a.recycle();
+ }
}
/**
diff --git a/packages/SettingsLib/UsageProgressBarPreference/Android.bp b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
index f346a5962d1c..3331550d0535 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/Android.bp
+++ b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
@@ -6,6 +6,7 @@ android_library {
static_libs: [
"androidx.preference_preference",
+ "androidx-constraintlayout_constraintlayout",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
index 9dbd5fa4b2d2..f45105dc3311 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
+++ b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
@@ -17,6 +17,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
@@ -26,29 +27,40 @@
android:paddingTop="32dp"
android:paddingBottom="32dp">
- <RelativeLayout
+ <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/usage_summary"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_alignBaseline="@id/total_summary"
- android:singleLine="true"
+ app:layout_constraintWidth_percent="0.45"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBaseline_toBaselineOf="@id/total_summary"
android:ellipsize="marquee"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Display1"
- android:textSize="20sp"/>
+ android:textSize="14sp"
+ android:textAlignment="viewStart"/>
<TextView
android:id="@+id/total_summary"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:singleLine="true"
+ app:layout_constraintWidth_percent="0.45"
+ app:layout_constraintEnd_toStartOf="@id/custom_content"
android:ellipsize="marquee"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"/>
- </RelativeLayout>
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
+ android:textSize="14sp"
+ android:textAlignment="viewEnd"/>
+ <FrameLayout
+ android:id="@+id/custom_content"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="@id/total_summary"
+ app:layout_constraintWidth_percent="0.1"/>
+ </androidx.constraintlayout.widget.ConstraintLayout>
<ProgressBar
android:id="@android:id/progress"
diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
index 950a8b4ad7cc..af64a1dc2282 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
+++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
@@ -22,6 +22,9 @@ import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.RelativeSizeSpan;
import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -41,6 +44,7 @@ public class UsageProgressBarPreference extends Preference {
private CharSequence mUsageSummary;
private CharSequence mTotalSummary;
+ private ImageView mCustomImageView;
private int mPercent = -1;
/**
@@ -110,6 +114,15 @@ public class UsageProgressBarPreference extends Preference {
notifyChanged();
}
+ /** Set custom ImageView to the right side of total summary. */
+ public <T extends ImageView> void setCustomContent(T imageView) {
+ if (imageView == mCustomImageView) {
+ return;
+ }
+ mCustomImageView = imageView;
+ notifyChanged();
+ }
+
/**
* Binds the created View to the data for this preference.
*
@@ -141,6 +154,15 @@ public class UsageProgressBarPreference extends Preference {
progressBar.setIndeterminate(false);
progressBar.setProgress(mPercent);
}
+
+ final FrameLayout customLayout = (FrameLayout) holder.findViewById(R.id.custom_content);
+ if (mCustomImageView == null) {
+ customLayout.removeAllViews();
+ customLayout.setVisibility(View.GONE);
+ } else {
+ customLayout.addView(mCustomImageView);
+ customLayout.setVisibility(View.VISIBLE);
+ }
}
private CharSequence enlargeFontOfNumber(CharSequence summary) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 5580afe70faf..78282fb14f5f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -19,6 +19,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
@@ -38,7 +39,9 @@ import android.util.FeatureFlagUtils;
import com.android.settingslib.R;
import com.android.settingslib.Utils;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Track status of Wi-Fi for the Sys UI.
@@ -50,6 +53,7 @@ public class WifiStatusTracker {
private final NetworkScoreManager mNetworkScoreManager;
private final ConnectivityManager mConnectivityManager;
private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Set<Integer> mNetworks = new HashSet<>();
private final WifiNetworkScoreCache.CacheListener mCacheListener =
new WifiNetworkScoreCache.CacheListener(mHandler) {
@Override
@@ -64,6 +68,20 @@ public class WifiStatusTracker {
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onAvailable(
+ Network network, NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties, boolean blocked) {
+ boolean isVcnOverWifi =
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && (Utils.tryGetWifiInfoForVcn(networkCapabilities) != null);
+ boolean isWifi =
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
+ if (isVcnOverWifi || isWifi) {
+ mNetworks.add(network.getNetId());
+ }
+ }
+
// Note: onCapabilitiesChanged is guaranteed to be called "immediately" after onAvailable
// and onLinkPropertiesChanged.
@Override
@@ -84,9 +102,12 @@ public class WifiStatusTracker {
@Override
public void onLost(Network network) {
- updateWifiInfo(null);
- updateStatusLabel();
- mCallback.run();
+ if (mNetworks.contains(network.getNetId())) {
+ mNetworks.remove(network.getNetId());
+ updateWifiInfo(null);
+ updateStatusLabel();
+ mCallback.run();
+ }
}
};
private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
index 85e2174bf87a..1a8477d9b86e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
@@ -18,11 +18,15 @@ package com.android.settingslib.widget;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
import android.content.Context;
import android.text.SpannedString;
import android.text.style.RelativeSizeSpan;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -97,4 +101,30 @@ public class UsageProgressBarPreferenceTest {
.findViewById(android.R.id.progress);
assertThat(progressBar.getProgress()).isEqualTo((int) (31.0f / 80 * 100));
}
+
+ @Test
+ public void setCustomContent_setNullImageView_noChild() {
+ mUsageProgressBarPreference.setCustomContent(null /* imageView */);
+
+ mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+ final FrameLayout customContent =
+ (FrameLayout) mViewHolder.findViewById(R.id.custom_content);
+ assertThat(customContent.getChildCount()).isEqualTo(0);
+ assertThat(customContent.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setCustomContent_setImageView_oneChild() {
+ final ImageView imageView = mock(ImageView.class);
+ mUsageProgressBarPreference.setCustomContent(imageView);
+
+ mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+ final FrameLayout customContent =
+ (FrameLayout) mViewHolder.findViewById(R.id.custom_content);
+ assertThat(customContent.getChildCount()).isEqualTo(1);
+ assertThat(customContent.getChildAt(0)).isEqualTo(imageView);
+ assertThat(customContent.getVisibility()).isEqualTo(View.VISIBLE);
+ }
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index efd941e3bd18..438cec8ef68f 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -272,6 +272,7 @@ public class SettingsBackupTest {
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
Settings.Global.ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT,
+ Settings.Global.ENABLE_MULTI_SLOT_TIMEOUT_MILLIS,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
Settings.Global.ERROR_LOGCAT_PREFIX,
@@ -280,6 +281,7 @@ public class SettingsBackupTest {
Settings.Global.EUICC_UNSUPPORTED_COUNTRIES,
Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
+ Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 49f91096543a..01e54ff60582 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -31,4 +31,9 @@
<!-- AOD/Lockscreen alternate layout -->
<bool name="flag_keyguard_layout">false</bool>
+
+ <bool name="flag_brightness_slider">false</bool>
+
+ <!-- People Tile flag -->
+ <bool name="flag_conversations">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4baa06a393a8..cb7327f7629c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2556,8 +2556,8 @@
<!-- Text for privacy dialog, indicating that the application is the enterprise version [CHAR LIMIT=NONE] -->
<string name="ongoing_privacy_dialog_enterprise">(enterprise)</string>
- <!-- Text for privacy dialog, identifying the phonecall app [CHAR LIMIT=NONE]-->
- <string name="ongoing_privacy_dialog_phonecall">Phonecall</string>
+ <!-- Text for privacy dialog, identifying the phone call app [CHAR LIMIT=NONE]-->
+ <string name="ongoing_privacy_dialog_phonecall">Phone call</string>
<!-- Text for privacy dialog, indicating that an app is using an op on behalf of another [CHAR LIMIT=NONE] -->
<string name="ongoing_privacy_dialog_attribution_text">(through <xliff:g id="attribution" example="Special app">%s</xliff:g>)</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 2a0715ed764c..87f6b8202ded 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -118,9 +118,9 @@ public class RemoteAnimationTargetCompat {
}
public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) {
- final RemoteAnimationTargetCompat[] appsCompat =
- new RemoteAnimationTargetCompat[apps != null ? apps.length : 0];
- for (int i = 0; i < apps.length; i++) {
+ final int length = apps != null ? apps.length : 0;
+ final RemoteAnimationTargetCompat[] appsCompat = new RemoteAnimationTargetCompat[length];
+ for (int i = 0; i < length; i++) {
appsCompat[i] = new RemoteAnimationTargetCompat(apps[i]);
}
return appsCompat;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9908e672535f..d9a1eb6c0b28 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1909,12 +1909,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
/**
- * Whether to show the lock icon on lock screen and bouncer. This depends on the enrolled
- * biometrics to the device.
+ * Whether to show the lock icon on lock screen and bouncer.
*/
- public boolean shouldShowLockIcon() {
- return isFaceAuthEnabledForUser(KeyguardUpdateMonitor.getCurrentUser())
- && !isUdfpsEnrolled();
+ public boolean canShowLockIcon() {
+ if (mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1) {
+ return isFaceAuthEnabledForUser(KeyguardUpdateMonitor.getCurrentUser())
+ && !isUdfpsEnrolled();
+ }
+ return true;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 036fcf3bec27..78f7966558ab 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -127,7 +127,7 @@ public class SystemUIApplication extends Application implements
}
// If SHOW_PEOPLE_SPACE is true, enable People Space widget provider.
- // TODO(b/170396074): Remove this when we don't need a widget anymore.
+ // TODO(b/170396074): Migrate to new feature flag (go/silk-flags-howto)
try {
int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.SHOW_PEOPLE_SPACE, 1);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index d6413ed63e6e..0a7eea487ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -602,7 +602,7 @@ public class ScreenshotController {
private void runScrollCapture(ScrollCaptureClient.Connection connection) {
cancelTimeout();
ScrollCaptureController controller = new ScrollCaptureController(mContext, connection,
- mMainExecutor, mBgExecutor, mImageExporter);
+ mMainExecutor, mBgExecutor, mImageExporter, mUiEventLogger);
controller.attach(mWindow);
controller.start(new TakeScreenshotService.RequestCallback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index f1fb6884188f..5cf018813133 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -63,7 +63,15 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "screenshot swiped to dismiss")
SCREENSHOT_SWIPE_DISMISSED(656),
@UiEvent(doc = "screenshot reentered for new screenshot")
- SCREENSHOT_REENTERED(640);
+ SCREENSHOT_REENTERED(640),
+ @UiEvent(doc = "Long screenshot button was shown to the user")
+ SCREENSHOT_LONG_SCREENSHOT_IMPRESSION(687),
+ @UiEvent(doc = "User has requested a long screenshot")
+ SCREENSHOT_LONG_SCREENSHOT_REQUESTED(688),
+ @UiEvent(doc = "User has shared a long screenshot")
+ SCREENSHOT_LONG_SCREENSHOT_SHARE(689),
+ @UiEvent(doc = "User has sent a long screenshot to the editor")
+ SCREENSHOT_LONG_SCREENSHOT_EDIT(690);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index bf86b68893c8..3bc5ebf8c64e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -192,14 +192,14 @@ public class ScreenshotView extends FrameLayout implements
if (DEBUG_SCROLL) {
Log.d(TAG, "Showing Scroll option");
}
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION);
mScrollChip.setVisibility(VISIBLE);
mScrollChip.setOnClickListener((v) -> {
if (DEBUG_INPUT) {
Log.d(TAG, "scroll chip tapped");
}
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_REQUESTED);
onClick.run();
- // TODO Logging, store event consumer to a field
- //onElementTapped.accept(ScreenshotEvent.SCREENSHOT_SCROLL_TAPPED);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 9be3566e1f63..176a2c78796b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -29,6 +29,7 @@ import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.Window;
import android.widget.ImageView;
+import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
@@ -58,6 +59,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
private final Executor mBgExecutor;
private final ImageExporter mImageExporter;
private final ImageTileSet mImageTileSet;
+ private final UiEventLogger mUiEventLogger;
private ZonedDateTime mCaptureTime;
private UUID mRequestId;
@@ -72,12 +74,13 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
private Runnable mPendingAction;
public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
- Executor bgExecutor, ImageExporter exporter) {
+ Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) {
mContext = context;
mConnection = connection;
mUiExecutor = uiExecutor;
mBgExecutor = bgExecutor;
mImageExporter = exporter;
+ mUiEventLogger = uiEventLogger;
mImageTileSet = new ImageTileSet();
}
@@ -136,10 +139,12 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
disableButtons();
finish();
} else if (id == R.id.edit) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT);
v.setPressed(true);
disableButtons();
edit();
} else if (id == R.id.share) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE);
v.setPressed(true);
disableButtons();
share();
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
index 11ee94c97d7c..8dcc8b46f024 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
@@ -17,6 +17,7 @@
package com.android.systemui.settings.brightness;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
@@ -28,11 +29,13 @@ import javax.inject.Inject;
public class BrightnessControllerSettings {
private static final String THICK_BRIGHTNESS_SLIDER = "sysui_thick_brightness";
+ private final FeatureFlags mFeatureFlags;
private final boolean mUseThickSlider;
private final boolean mUseMirrorOnThickSlider;
@Inject
- public BrightnessControllerSettings(SecureSettings settings) {
+ public BrightnessControllerSettings(SecureSettings settings, FeatureFlags featureFlags) {
+ mFeatureFlags = featureFlags;
mUseThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 0;
mUseMirrorOnThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 2;
}
@@ -41,11 +44,11 @@ public class BrightnessControllerSettings {
// restart systemui after changing it.
/** */
boolean useThickSlider() {
- return mUseThickSlider;
+ return mUseThickSlider && mFeatureFlags.useNewBrightnessSlider();
}
/** */
boolean useMirrorOnThickSlider() {
- return mUseMirrorOnThickSlider;
+ return !useThickSlider() || (useThickSlider() && mUseMirrorOnThickSlider);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 30b31580e130..e7b60c3a0d67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -61,4 +61,13 @@ public class FeatureFlags {
public boolean isKeyguardLayoutEnabled() {
return mFlagReader.isEnabled(R.bool.flag_keyguard_layout);
}
+
+ /** b/178485354 */
+ public boolean useNewBrightnessSlider() {
+ return mFlagReader.isEnabled(R.bool.flag_brightness_slider);
+ }
+
+ public boolean isPeopleTileEnabled() {
+ return mFlagReader.isEnabled(R.bool.flag_conversations);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java b/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
index 1ec043cb7670..e4ae560ba69b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
@@ -23,8 +23,6 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
-import com.android.systemui.statusbar.phone.LockIcon;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
@@ -41,7 +39,6 @@ public class SuperStatusBarViewFactory {
private final Context mContext;
private final InjectionInflationController mInjectionInflationController;
- private final LockscreenLockIconController mLockIconController;
private final NotificationShelfComponent.Builder mNotificationShelfComponentBuilder;
private NotificationShadeWindowView mNotificationShadeWindowView;
@@ -51,11 +48,9 @@ public class SuperStatusBarViewFactory {
@Inject
public SuperStatusBarViewFactory(Context context,
InjectionInflationController injectionInflationController,
- NotificationShelfComponent.Builder notificationShelfComponentBuilder,
- LockscreenLockIconController lockIconController) {
+ NotificationShelfComponent.Builder notificationShelfComponentBuilder) {
mContext = context;
mInjectionInflationController = injectionInflationController;
- mLockIconController = lockIconController;
mNotificationShelfComponentBuilder = notificationShelfComponentBuilder;
}
@@ -77,10 +72,6 @@ public class SuperStatusBarViewFactory {
throw new IllegalStateException(
"R.layout.super_notification_shade could not be properly inflated");
}
- LockIcon lockIcon = mNotificationShadeWindowView.findViewById(R.id.lock_icon);
- if (lockIcon != null) {
- mLockIconController.attach(lockIcon);
- }
return mNotificationShadeWindowView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index 4fde1187477c..db49e4476a99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -21,7 +21,6 @@ import android.util.Log;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
@@ -50,7 +49,6 @@ public class NotifPipelineInitializer implements Dumpable {
private final ShadeListBuilder mListBuilder;
private final NotifCoordinators mNotifPluggableCoordinators;
private final NotifInflaterImpl mNotifInflater;
- private final PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
private final DumpManager mDumpManager;
private final ShadeViewManagerFactory mShadeViewManagerFactory;
private final FeatureFlags mFeatureFlags;
@@ -64,7 +62,6 @@ public class NotifPipelineInitializer implements Dumpable {
ShadeListBuilder listBuilder,
NotifCoordinators notifCoordinators,
NotifInflaterImpl notifInflater,
- PeopleSpaceWidgetManager peopleSpaceWidgetManager,
DumpManager dumpManager,
ShadeViewManagerFactory shadeViewManagerFactory,
FeatureFlags featureFlags) {
@@ -75,7 +72,6 @@ public class NotifPipelineInitializer implements Dumpable {
mNotifPluggableCoordinators = notifCoordinators;
mDumpManager = dumpManager;
mNotifInflater = notifInflater;
- mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
mShadeViewManagerFactory = shadeViewManagerFactory;
mFeatureFlags = featureFlags;
}
@@ -103,7 +99,6 @@ public class NotifPipelineInitializer implements Dumpable {
mListBuilder.attach(mNotifCollection);
mNotifCollection.attach(mGroupCoalescer);
mGroupCoalescer.attach(notificationService);
- mPeopleSpaceWidgetManager.attach(notificationService);
Log.d(TAG, "Notif pipeline initialized");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 54ce4ede9770..0ad6507fb01e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.init
import android.service.notification.StatusBarNotification
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.people.widget.PeopleSpaceWidgetManager
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.NotificationListener
@@ -73,7 +74,8 @@ class NotificationsControllerImpl @Inject constructor(
private val headsUpController: HeadsUpController,
private val headsUpViewBinder: HeadsUpViewBinder,
private val clickerBuilder: NotificationClicker.Builder,
- private val animatedImageNotificationManager: AnimatedImageNotificationManager
+ private val animatedImageNotificationManager: AnimatedImageNotificationManager,
+ private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager
) : NotificationsController {
override fun initialize(
@@ -126,6 +128,10 @@ class NotificationsControllerImpl @Inject constructor(
entryManager.attach(notificationListener)
}
+
+ if (featureFlags.isPeopleTileEnabled) {
+ peopleSpaceWidgetManager.attach(notificationListener)
+ }
}
override fun dump(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index e1eaf3c17357..f289b9f20211 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -71,7 +71,7 @@ public final class DozeServiceHost implements DozeHost {
"persist.sysui.wake_performs_auth", true);
private boolean mDozingRequested;
private boolean mPulsing;
- private WakefulnessLifecycle mWakefulnessLifecycle;
+ private final WakefulnessLifecycle mWakefulnessLifecycle;
private final SysuiStatusBarStateController mStatusBarStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
@@ -86,9 +86,8 @@ public final class DozeServiceHost implements DozeHost {
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
- private final LockscreenLockIconController mLockscreenLockIconController;
private final AuthController mAuthController;
- private NotificationIconAreaController mNotificationIconAreaController;
+ private final NotificationIconAreaController mNotificationIconAreaController;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private NotificationPanelViewController mNotificationPanel;
private View mAmbientIndicationContainer;
@@ -109,7 +108,6 @@ public final class DozeServiceHost implements DozeHost {
PulseExpansionHandler pulseExpansionHandler,
NotificationShadeWindowController notificationShadeWindowController,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- LockscreenLockIconController lockscreenLockIconController,
AuthController authController,
NotificationIconAreaController notificationIconAreaController) {
super();
@@ -129,7 +127,6 @@ public final class DozeServiceHost implements DozeHost {
mPulseExpansionHandler = pulseExpansionHandler;
mNotificationShadeWindowController = notificationShadeWindowController;
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
- mLockscreenLockIconController = lockscreenLockIconController;
mAuthController = authController;
mNotificationIconAreaController = notificationIconAreaController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index eceac3240ebc..4b70de997906 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -37,12 +37,10 @@ import androidx.annotation.Nullable;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -52,18 +50,20 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator.WakeUpListener;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.ViewController;
import java.util.Optional;
import javax.inject.Inject;
-/** Controls the {@link LockIcon} in the lockscreen. */
-@SysUISingleton
-public class LockscreenLockIconController {
+/** Controls the {@link LockIcon} on the lockscreen. */
+@StatusBarComponent.StatusBarScope
+public class LockscreenLockIconController extends ViewController<LockIcon> {
private final LockscreenGestureLogger mLockscreenGestureLogger;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -79,7 +79,6 @@ public class LockscreenLockIconController {
private final KeyguardStateController mKeyguardStateController;
private final Resources mResources;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
- private final KeyguardSecurityModel mKeyguardSecurityModel;
private boolean mKeyguardShowing;
private boolean mKeyguardJustShown;
private boolean mBlockUpdates;
@@ -92,231 +91,13 @@ public class LockscreenLockIconController {
private boolean mBouncerShowingScrimmed;
private boolean mFingerprintUnlock;
private int mStatusBarState = StatusBarState.SHADE;
- private LockIcon mLockIcon;
-
- private View.OnAttachStateChangeListener mOnAttachStateChangeListener =
- new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- mStatusBarStateController.addCallback(mSBStateListener);
- mConfigurationController.addCallback(mConfigurationListener);
- mNotificationWakeUpCoordinator.addListener(mWakeUpListener);
- mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
- mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
-
- mDockManager.ifPresent(dockManager -> dockManager.addListener(mDockEventListener));
-
- mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
- mConfigurationListener.onThemeChanged();
-
- updateColor();
- update();
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mStatusBarStateController.removeCallback(mSBStateListener);
- mConfigurationController.removeCallback(mConfigurationListener);
- mNotificationWakeUpCoordinator.removeListener(mWakeUpListener);
- mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
- mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
-
- mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener));
- }
- };
-
- private final StatusBarStateController.StateListener mSBStateListener =
- new StatusBarStateController.StateListener() {
- @Override
- public void onDozingChanged(boolean isDozing) {
- setDozing(isDozing);
- }
-
- @Override
- public void onDozeAmountChanged(float linear, float eased) {
- if (mLockIcon != null) {
- mLockIcon.setDozeAmount(eased);
- }
- }
-
- @Override
- public void onStateChanged(int newState) {
- setStatusBarState(newState);
- }
- };
-
- private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
- private int mDensity;
-
- @Override
- public void onUiModeChanged() {
- updateColor();
- }
-
- @Override
- public void onOverlayChanged() {
- updateColor();
- }
-
- @Override
- public void onDensityOrFontScaleChanged() {
- if (mLockIcon == null) {
- return;
- }
-
- ViewGroup.LayoutParams lp = mLockIcon.getLayoutParams();
- if (lp == null) {
- return;
- }
- lp.width = mLockIcon.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_width);
- lp.height = mLockIcon.getResources().getDimensionPixelSize(
- R.dimen.keyguard_lock_height);
- mLockIcon.setLayoutParams(lp);
- update(true /* force */);
- }
-
- @Override
- public void onLocaleListChanged() {
- if (mLockIcon == null) {
- return;
- }
-
- mLockIcon.setContentDescription(
- mLockIcon.getResources().getText(R.string.accessibility_unlock_button));
- update(true /* force */);
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- final int density = newConfig.densityDpi;
- if (density != mDensity) {
- mDensity = density;
- update();
- }
- }
- };
-
- private final WakeUpListener mWakeUpListener = new WakeUpListener() {
- @Override
- public void onPulseExpansionChanged(boolean expandingChanged) {
- }
-
- @Override
- public void onFullyHiddenChanged(boolean isFullyHidden) {
- if (mKeyguardBypassController.getBypassEnabled()) {
- boolean changed = updateIconVisibility();
- if (changed) {
- update();
- }
- }
- }
- };
-
- private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onSimStateChanged(int subId, int slotId, int simState) {
- mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
- update();
- }
-
- @Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- update();
- }
-
- @Override
- public void onBiometricRunningStateChanged(boolean running,
- BiometricSourceType biometricSourceType) {
- update();
- }
-
- @Override
- public void onStrongAuthStateChanged(int userId) {
- update();
- }
- };
-
- private final DockManager.DockEventListener mDockEventListener =
- event -> {
- boolean docked =
- event == DockManager.STATE_DOCKED || event == DockManager.STATE_DOCKED_HIDE;
- if (docked != mDocked) {
- mDocked = docked;
- update();
- }
- };
-
- private final KeyguardStateController.Callback mKeyguardMonitorCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- boolean force = false;
- boolean wasShowing = mKeyguardShowing;
- mKeyguardShowing = mKeyguardStateController.isShowing();
- if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
- mBlockUpdates = false;
- force = true;
- }
- if (!wasShowing && mKeyguardShowing) {
- setBouncerHideAmount(KeyguardBouncer.EXPANSION_HIDDEN);
- mKeyguardJustShown = true;
- }
- update(force);
- }
-
- @Override
- public void onKeyguardFadingAwayChanged() {
- if (!mKeyguardStateController.isKeyguardFadingAway()) {
- if (mBlockUpdates) {
- mBlockUpdates = false;
- update(true /* force */);
- }
- }
- }
-
- @Override
- public void onUnlockedChanged() {
- update();
- }
- };
-
- private final View.AccessibilityDelegate mAccessibilityDelegate =
- new View.AccessibilityDelegate() {
- @Override
- public void onInitializeAccessibilityNodeInfo(View host,
- AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- boolean fingerprintRunning =
- mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
- // Only checking if unlocking with Biometric is allowed (no matter strong or
- // non-strong as long as primary auth, i.e. PIN/pattern/password, is not
- // required), so it's ok to pass true for isStrongBiometric to
- // isUnlockingWithBiometricAllowed() to bypass the check of whether non-strong
- // biometric is allowed
- boolean unlockingAllowed = mKeyguardUpdateMonitor
- .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
- if (fingerprintRunning && unlockingAllowed) {
- AccessibilityNodeInfo.AccessibilityAction unlock =
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLICK,
- mResources.getString(
- R.string.accessibility_unlock_without_fingerprint));
- info.addAction(unlock);
- info.setHintText(mResources.getString(
- R.string.accessibility_waiting_for_fingerprint));
- } else if (getState() == STATE_SCANNING_FACE) {
- //Avoid 'button' to be spoken for scanning face
- info.setClassName(LockIcon.class.getName());
- info.setContentDescription(mResources.getString(
- R.string.accessibility_scanning_face));
- }
- }
- };
private int mLastState;
+ private boolean mDozing;
@Inject
- public LockscreenLockIconController(LockscreenGestureLogger lockscreenGestureLogger,
+ public LockscreenLockIconController(
+ @Nullable LockIcon view,
+ LockscreenGestureLogger lockscreenGestureLogger,
KeyguardUpdateMonitor keyguardUpdateMonitor,
LockPatternUtils lockPatternUtils,
ShadeController shadeController,
@@ -329,8 +110,8 @@ public class LockscreenLockIconController {
@Nullable DockManager dockManager,
KeyguardStateController keyguardStateController,
@Main Resources resources,
- HeadsUpManagerPhone headsUpManagerPhone,
- KeyguardSecurityModel keyguardSecurityModel) {
+ HeadsUpManagerPhone headsUpManagerPhone) {
+ super(view);
mLockscreenGestureLogger = lockscreenGestureLogger;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
@@ -345,32 +126,52 @@ public class LockscreenLockIconController {
mKeyguardStateController = keyguardStateController;
mResources = resources;
mHeadsUpManagerPhone = headsUpManagerPhone;
- mKeyguardSecurityModel = keyguardSecurityModel;
+
+ if (view == null) {
+ return;
+ }
mKeyguardIndicationController.setLockIconController(this);
}
- /**
- * Associate the controller with a {@link LockIcon}
- *
- * TODO: change to an init method and inject the view.
- */
- public void attach(LockIcon lockIcon) {
- mLockIcon = lockIcon;
-
- mLockIcon.setOnClickListener(this::handleClick);
- mLockIcon.setOnLongClickListener(this::handleLongClick);
- mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
-
- if (mLockIcon.isAttachedToWindow()) {
- mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
+ @Override
+ protected void onInit() {
+ if (mView == null) {
+ return;
}
- mLockIcon.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
+ mView.setOnClickListener(this::handleClick);
+ mView.setOnLongClickListener(this::handleLongClick);
+ mView.setAccessibilityDelegate(mAccessibilityDelegate);
+ }
+
+ @Override
+ protected void onViewAttached() {
setStatusBarState(mStatusBarStateController.getState());
+ mDozing = mStatusBarStateController.isDozing();
+ mStatusBarStateController.addCallback(mSBStateListener);
+ mConfigurationController.addCallback(mConfigurationListener);
+ mNotificationWakeUpCoordinator.addListener(mWakeUpListener);
+ mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+ mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
+
+ mDockManager.ifPresent(dockManager -> dockManager.addListener(mDockEventListener));
+
+ mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
+ mConfigurationListener.onThemeChanged();
+
+ updateColor();
+ update();
}
- public LockIcon getView() {
- return mLockIcon;
+ @Override
+ protected void onViewDetached() {
+ mStatusBarStateController.removeCallback(mSBStateListener);
+ mConfigurationController.removeCallback(mConfigurationListener);
+ mNotificationWakeUpCoordinator.removeListener(mWakeUpListener);
+ mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
+ mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
+
+ mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener));
}
/**
@@ -430,31 +231,30 @@ public class LockscreenLockIconController {
}
private void updateColor() {
- if (mLockIcon == null) {
+ if (mView == null) {
return;
}
-
int iconColor = -1;
if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_VISIBLE) {
- TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes(
+ TypedArray typedArray = mView.getContext().getTheme().obtainStyledAttributes(
null, new int[]{ android.R.attr.textColorPrimary }, 0, 0);
iconColor = typedArray.getColor(0, Color.WHITE);
typedArray.recycle();
} else if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_HIDDEN) {
iconColor = Utils.getColorAttrDefaultColor(
- mLockIcon.getContext(), com.android.systemui.R.attr.wallpaperTextColor);
+ mView.getContext(), com.android.systemui.R.attr.wallpaperTextColor);
} else {
// bouncer is transitioning
- TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes(
+ TypedArray typedArray = mView.getContext().getTheme().obtainStyledAttributes(
null, new int[]{ android.R.attr.textColorPrimary }, 0, 0);
int bouncerIconColor = typedArray.getColor(0, Color.WHITE);
typedArray.recycle();
int keyguardIconColor = Utils.getColorAttrDefaultColor(
- mLockIcon.getContext(), com.android.systemui.R.attr.wallpaperTextColor);
+ mView.getContext(), com.android.systemui.R.attr.wallpaperTextColor);
iconColor = (int) new ArgbEvaluator().evaluate(
mBouncerHiddenAmount, bouncerIconColor, keyguardIconColor);
}
- mLockIcon.updateColor(iconColor);
+ mView.updateColor(iconColor);
}
/**
@@ -497,14 +297,16 @@ public class LockscreenLockIconController {
}
private void update(boolean force) {
+ if (mView == null) {
+ return;
+ }
int state = getState();
boolean shouldUpdate = mLastState != state || force;
if (mBlockUpdates && canBlockUpdates()) {
shouldUpdate = false;
}
- if (shouldUpdate && mLockIcon != null && mLockIcon.getVisibility() != GONE) {
- mLockIcon.update(state,
- mStatusBarStateController.isDozing(), mKeyguardJustShown);
+ if (shouldUpdate && mView.getVisibility() != GONE) {
+ mView.update(state, mDozing, mKeyguardJustShown);
}
mLastState = state;
mKeyguardJustShown = false;
@@ -531,10 +333,6 @@ public class LockscreenLockIconController {
return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
}
- private void setDozing(boolean isDozing) {
- update();
- }
-
/** Set the StatusBarState. */
private void setStatusBarState(int statusBarState) {
mStatusBarState = statusBarState;
@@ -546,17 +344,16 @@ public class LockscreenLockIconController {
* @return true if the visibility changed
*/
private boolean updateIconVisibility() {
- if (mLockIcon == null) {
+ if (mView == null) {
return false;
}
-
- if (!mKeyguardUpdateMonitor.shouldShowLockIcon()) {
- boolean changed = mLockIcon.getVisibility() != GONE;
- mLockIcon.setVisibility(GONE);
+ if (!mKeyguardUpdateMonitor.canShowLockIcon()) {
+ boolean changed = mView.getVisibility() != GONE;
+ mView.setVisibility(GONE);
return changed;
}
- boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked;
+ boolean onAodOrDocked = mDozing || mDocked;
boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance;
boolean fingerprintOrBypass = mFingerprintUnlock
|| mKeyguardBypassController.getBypassEnabled();
@@ -569,21 +366,202 @@ public class LockscreenLockIconController {
invisible = true;
}
}
- return mLockIcon.updateIconVisibility(!invisible);
+ return mView.updateIconVisibility(!invisible);
}
private void updateClickability() {
- if (mAccessibilityController == null) {
+ if (mView == null) {
return;
}
boolean canLock = mKeyguardStateController.isMethodSecure()
&& mKeyguardStateController.canDismissLockScreen();
boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
- if (mLockIcon != null) {
- mLockIcon.setClickable(clickToUnlock);
- mLockIcon.setLongClickable(canLock && !clickToUnlock);
- mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled());
- }
+ mView.setClickable(clickToUnlock);
+ mView.setLongClickable(canLock && !clickToUnlock);
+ mView.setFocusable(mAccessibilityController.isAccessibilityEnabled());
}
+ private final StatusBarStateController.StateListener mSBStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ if (mDozing != isDozing) {
+ mDozing = isDozing;
+ update();
+ }
+ }
+
+ @Override
+ public void onDozeAmountChanged(float linear, float eased) {
+ if (mView != null) {
+ mView.setDozeAmount(eased);
+ }
+ }
+
+ @Override
+ public void onStateChanged(int newState) {
+ setStatusBarState(newState);
+ }
+ };
+
+ private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+ private int mDensity;
+
+ @Override
+ public void onUiModeChanged() {
+ updateColor();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ updateColor();
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ ViewGroup.LayoutParams lp = mView.getLayoutParams();
+ if (lp == null) {
+ return;
+ }
+ lp.width = mView.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_width);
+ lp.height = mView.getResources().getDimensionPixelSize(
+ R.dimen.keyguard_lock_height);
+ mView.setLayoutParams(lp);
+ update(true /* force */);
+ }
+
+ @Override
+ public void onLocaleListChanged() {
+ mView.setContentDescription(
+ mView.getResources().getText(R.string.accessibility_unlock_button));
+ update(true /* force */);
+ }
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ final int density = newConfig.densityDpi;
+ if (density != mDensity) {
+ mDensity = density;
+ update();
+ }
+ }
+ };
+
+ private final WakeUpListener mWakeUpListener = new WakeUpListener() {
+ @Override
+ public void onPulseExpansionChanged(boolean expandingChanged) {
+ }
+
+ @Override
+ public void onFullyHiddenChanged(boolean isFullyHidden) {
+ if (mKeyguardBypassController.getBypassEnabled()) {
+ boolean changed = updateIconVisibility();
+ if (changed) {
+ update();
+ }
+ }
+ }
+ };
+
+ private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onSimStateChanged(int subId, int slotId, int simState) {
+ mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
+ update();
+ }
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ update();
+ }
+
+ @Override
+ public void onBiometricRunningStateChanged(boolean running,
+ BiometricSourceType biometricSourceType) {
+ update();
+ }
+
+ @Override
+ public void onStrongAuthStateChanged(int userId) {
+ update();
+ }
+ };
+
+ private final DockManager.DockEventListener mDockEventListener =
+ event -> {
+ boolean docked =
+ event == DockManager.STATE_DOCKED || event == DockManager.STATE_DOCKED_HIDE;
+ if (docked != mDocked) {
+ mDocked = docked;
+ update();
+ }
+ };
+
+ private final KeyguardStateController.Callback mKeyguardMonitorCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ boolean force = false;
+ boolean wasShowing = mKeyguardShowing;
+ mKeyguardShowing = mKeyguardStateController.isShowing();
+ if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
+ mBlockUpdates = false;
+ force = true;
+ }
+ if (!wasShowing && mKeyguardShowing) {
+ setBouncerHideAmount(KeyguardBouncer.EXPANSION_HIDDEN);
+ mKeyguardJustShown = true;
+ }
+ update(force);
+ }
+
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+ if (!mKeyguardStateController.isKeyguardFadingAway()) {
+ if (mBlockUpdates) {
+ mBlockUpdates = false;
+ update(true /* force */);
+ }
+ }
+ }
+
+ @Override
+ public void onUnlockedChanged() {
+ update();
+ }
+ };
+
+ private final View.AccessibilityDelegate mAccessibilityDelegate =
+ new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ boolean fingerprintRunning =
+ mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
+ // Only checking if unlocking with Biometric is allowed (no matter strong or
+ // non-strong as long as primary auth, i.e. PIN/pattern/password, is not
+ // required), so it's ok to pass true for isStrongBiometric to
+ // isUnlockingWithBiometricAllowed() to bypass the check of whether non-strong
+ // biometric is allowed
+ boolean unlockingAllowed = mKeyguardUpdateMonitor
+ .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
+ if (fingerprintRunning && unlockingAllowed) {
+ AccessibilityNodeInfo.AccessibilityAction unlock =
+ new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK,
+ mResources.getString(
+ R.string.accessibility_unlock_without_fingerprint));
+ info.addAction(unlock);
+ info.setHintText(mResources.getString(
+ R.string.accessibility_waiting_for_fingerprint));
+ } else if (getState() == STATE_SCANNING_FACE) {
+ //Avoid 'button' to be spoken for scanning face
+ info.setClassName(LockIcon.class.getName());
+ info.setContentDescription(mResources.getString(
+ R.string.accessibility_scanning_face));
+ }
+ }
+ };
}
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 b24d0e7762ad..a5284f1421a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -910,7 +910,7 @@ public class NotificationPanelViewController extends PanelViewController {
clockPreferredY, hasCustomClock(),
hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
- mUpdateMonitor.shouldShowLockIcon(),
+ mUpdateMonitor.canShowLockIcon(),
getQsExpansionFraction(),
mDisplayCutoutTopInset);
mClockPositionAlgorithm.run(mClockPositionResult);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 83651398be43..4a3d8d67e85d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -368,7 +368,6 @@ public class StatusBar extends SystemUI implements DemoMode,
protected NotificationShadeWindowController mNotificationShadeWindowController;
protected StatusBarWindowController mStatusBarWindowController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final LockscreenLockIconController mLockscreenLockIconController;
@VisibleForTesting
DozeServiceHost mDozeServiceHost;
private boolean mWakeUpComingFromTouch;
@@ -415,6 +414,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// expanded notifications
// the sliding/resizing panel within the notification window
protected NotificationPanelViewController mNotificationPanelViewController;
+ protected LockscreenLockIconController mLockscreenLockIconController;
// settings
private QSPanelController mQSPanelController;
@@ -725,7 +725,6 @@ public class StatusBar extends SystemUI implements DemoMode,
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
- LockscreenLockIconController lockscreenLockIconController,
DozeParameters dozeParameters,
ScrimController scrimController,
@Nullable KeyguardLiftController keyguardLiftController,
@@ -807,7 +806,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mAssistManagerLazy = assistManagerLazy;
mConfigurationController = configurationController;
mNotificationShadeWindowController = notificationShadeWindowController;
- mLockscreenLockIconController = lockscreenLockIconController;
mDozeServiceHost = dozeServiceHost;
mPowerManager = powerManager;
mDozeParameters = dozeParameters;
@@ -1173,9 +1171,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mScrimController.setScrimVisibleListener(scrimsVisible -> {
mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible);
- if (mNotificationShadeWindowView != null) {
- mLockscreenLockIconController.onScrimVisibilityChanged(scrimsVisible);
- }
+ mLockscreenLockIconController.onScrimVisibilityChanged(scrimsVisible);
});
mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble);
@@ -1208,9 +1204,6 @@ public class StatusBar extends SystemUI implements DemoMode,
createUserSwitcher();
}
- mNotificationPanelViewController.setLaunchAffordanceListener(
- mLockscreenLockIconController::onShowingLaunchAffordanceChanged);
-
// Set up the quick settings tile panel
final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
if (container != null) {
@@ -1490,6 +1483,11 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarWindowController = statusBarComponent.getStatusBarWindowController();
mPhoneStatusBarWindow = mSuperStatusBarViewFactory.getStatusBarWindowView();
mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
+ mLockscreenLockIconController = statusBarComponent.getLockscreenLockIconController();
+ mLockscreenLockIconController.init();
+
+ mNotificationPanelViewController.setLaunchAffordanceListener(
+ mLockscreenLockIconController::onShowingLaunchAffordanceChanged);
}
protected void startKeyguard() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 5f90077640ec..f6165f666c89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -316,11 +316,23 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
mIconController.removeAllIconsForSlot(mSlotMobile);
mMobileStates.clear();
+ List<NoCallingIconState> noCallingStates = new ArrayList<NoCallingIconState>();
+ noCallingStates.addAll(mNoCallingStates);
mNoCallingStates.clear();
final int n = subs.size();
for (int i = 0; i < n; i++) {
mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId()));
- mNoCallingStates.add(new NoCallingIconState(subs.get(i).getSubscriptionId()));
+ boolean isNewSub = true;
+ for (NoCallingIconState state : noCallingStates) {
+ if (state.subId == subs.get(i).getSubscriptionId()) {
+ mNoCallingStates.add(state);
+ isNewSub = false;
+ break;
+ }
+ }
+ if (isNewSub) {
+ mNoCallingStates.add(new NoCallingIconState(subs.get(i).getSubscriptionId()));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index 802da3e8c21c..ecd9613f84b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
@@ -73,4 +74,9 @@ public interface StatusBarComponent {
@StatusBarScope
NotificationPanelViewController getNotificationPanelViewController();
+ /**
+ * Creates a LockscreenLockIconController.
+ */
+ @StatusBarScope
+ LockscreenLockIconController getLockscreenLockIconController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 26e19596b577..9e9533d0e199 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -77,7 +77,6 @@ import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LightsOutNotifController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
@@ -167,7 +166,6 @@ public interface StatusBarPhoneModule {
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
- LockscreenLockIconController lockscreenLockIconController,
DozeParameters dozeParameters,
ScrimController scrimController,
@Nullable KeyguardLiftController keyguardLiftController,
@@ -248,7 +246,6 @@ public interface StatusBarPhoneModule {
assistManagerLazy,
configurationController,
notificationShadeWindowController,
- lockscreenLockIconController,
dozeParameters,
scrimController,
keyguardLiftController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 37d8c9ae2958..781abe6cef3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -16,6 +16,10 @@
package com.android.systemui.statusbar.phone.dagger;
+import android.annotation.Nullable;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
@@ -32,4 +36,12 @@ public abstract class StatusBarViewModule {
return notificationShadeWindowView.getNotificationPanelView();
}
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ @Nullable
+ public static LockIcon getLockIcon(
+ NotificationShadeWindowView notificationShadeWindowView) {
+ return notificationShadeWindowView.findViewById(R.id.lock_icon);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 23c093033ae0..bdde82289e86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -88,7 +88,6 @@ public class DozeServiceHostTest extends SysuiTestCase {
@Mock private NotificationPanelViewController mNotificationPanel;
@Mock private View mAmbientIndicationContainer;
@Mock private BiometricUnlockController mBiometricUnlockController;
- @Mock private LockscreenLockIconController mLockscreenLockIconController;
@Mock private AuthController mAuthController;
@Before
@@ -100,7 +99,7 @@ public class DozeServiceHostTest extends SysuiTestCase {
mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController,
mKeyguardUpdateMonitor, mPulseExpansionHandler,
mNotificationShadeWindowController, mNotificationWakeUpCoordinator,
- mLockscreenLockIconController, mAuthController, mNotificationIconAreaController);
+ mAuthController, mNotificationIconAreaController);
mDozeServiceHost.initialize(
mStatusBar,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
index 95a35050c09e..60af16acbb7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -30,12 +29,12 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -45,6 +44,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -64,7 +64,7 @@ public class LockscreenIconControllerTest extends SysuiTestCase {
@Mock
private KeyguardIndicationController mKeyguardIndicationController;
@Mock
- private LockIcon mLockIcon; // TODO: make this not a mock once inject is removed.
+ private LockIcon mLockIcon;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
@@ -81,34 +81,36 @@ public class LockscreenIconControllerTest extends SysuiTestCase {
private Resources mResources;
@Mock
private HeadsUpManagerPhone mHeadsUpManagerPhone;
- @Mock
- private KeyguardSecurityModel mKeyguardSecurityModel;
private LockscreenLockIconController mLockIconController;
+
+ @Captor ArgumentCaptor<OnAttachStateChangeListener> mOnAttachStateChangeCaptor;
+ @Captor ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
+
private OnAttachStateChangeListener mOnAttachStateChangeListener;
+ private StatusBarStateController.StateListener mStatusBarStateListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mKeyguardUpdateMonitor.shouldShowLockIcon()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.canShowLockIcon()).thenReturn(true);
when(mLockIcon.getContext()).thenReturn(mContext);
- mLockIconController = new LockscreenLockIconController(
+ mLockIconController = new LockscreenLockIconController(mLockIcon,
mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils,
mShadeController, mAccessibilityController, mKeyguardIndicationController,
mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator,
mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources,
- mHeadsUpManagerPhone, mKeyguardSecurityModel);
+ mHeadsUpManagerPhone);
- ArgumentCaptor<OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
- ArgumentCaptor.forClass(OnAttachStateChangeListener.class);
+ when(mLockIcon.isAttachedToWindow()).thenReturn(true);
+ mLockIconController.init();
- doNothing().when(mLockIcon)
- .addOnAttachStateChangeListener(
- onAttachStateChangeListenerArgumentCaptor.capture());
- mLockIconController.attach(mLockIcon);
-
- mOnAttachStateChangeListener = onAttachStateChangeListenerArgumentCaptor.getValue();
+ verify(mLockIcon).addOnAttachStateChangeListener(
+ mOnAttachStateChangeCaptor.capture());
+ mOnAttachStateChangeListener = mOnAttachStateChangeCaptor.getValue();
+ verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture());
+ mStatusBarStateListener = mStateListenerCaptor.getValue();
}
@Test
@@ -133,23 +135,17 @@ public class LockscreenIconControllerTest extends SysuiTestCase {
@Test
public void testVisibility_Dozing() {
- ArgumentCaptor<StatusBarStateController.StateListener> sBStateListenerCaptor =
- ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
-
- mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
- verify(mStatusBarStateController).addCallback(sBStateListenerCaptor.capture());
-
when(mStatusBarStateController.isDozing()).thenReturn(true);
- sBStateListenerCaptor.getValue().onDozingChanged(true);
+ mStatusBarStateListener.onDozingChanged(true);
verify(mLockIcon).updateIconVisibility(false);
}
@Test
public void testVisibility_doNotShowLockIcon() {
- when(mKeyguardUpdateMonitor.shouldShowLockIcon()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.canShowLockIcon()).thenReturn(false);
+ mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
- mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
verify(mLockIcon).setVisibility(View.GONE);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 2c781bad10c2..cae488a561a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -386,7 +386,6 @@ public class StatusBarTest extends SysuiTestCase {
() -> mAssistManager,
configurationController,
mNotificationShadeWindowController,
- mLockscreenLockIconController,
mDozeParameters,
mScrimController,
mKeyguardLiftController,
@@ -436,6 +435,7 @@ public class StatusBarTest extends SysuiTestCase {
// initialized automatically.
mStatusBar.mNotificationShadeWindowView = mNotificationShadeWindowView;
mStatusBar.mNotificationPanelViewController = mNotificationPanelViewController;
+ mStatusBar.mLockscreenLockIconController = mLockscreenLockIconController;
mStatusBar.mDozeScrimController = mDozeScrimController;
mStatusBar.mPresenter = mNotificationPresenter;
mStatusBar.mKeyguardIndicationController = mKeyguardIndicationController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index da1f5d392872..f8b63835551f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -37,6 +37,7 @@ import static org.mockito.Mockito.when;
import android.app.Instrumentation;
import android.content.Intent;
import android.net.ConnectivityManager;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
@@ -343,6 +344,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
setConnectivityCommon(networkType, validated, isConnected);
if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
if (isConnected) {
+ mNetworkCallback.onAvailable(mock(Network.class),
+ new NetworkCapabilities(mNetCapabilities), new LinkProperties(), false);
mNetworkCallback.onCapabilitiesChanged(
mock(Network.class), new NetworkCapabilities(mNetCapabilities));
} else {
@@ -357,6 +360,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
setConnectivityCommon(networkType, validated, isConnected);
if (networkType == NetworkCapabilities.TRANSPORT_CELLULAR) {
if (isConnected) {
+ mNetworkCallback.onAvailable(mock(Network.class),
+ new NetworkCapabilities(mNetCapabilities), new LinkProperties(), false);
mNetworkCallback.onCapabilitiesChanged(
mock(Network.class), new NetworkCapabilities(mNetCapabilities));
} else {
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index ae024ff6d043..5dd271c9dbb1 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -260,6 +260,10 @@ message SystemMessage {
// Package: android
NOTE_ADB_WIFI_ACTIVE = 62;
+ // Notify the user a carrier suggestion is available to get IMSI exemption.
+ // Package: android
+ NOTE_CARRIER_SUGGESTION_AVAILABLE = 63;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/Android.bp b/services/Android.bp
index 1970b7d7b0dd..61591c2c29bd 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -28,6 +28,7 @@ filegroup {
":services.profcollect-sources",
":services.restrictions-sources",
":services.searchui-sources",
+ ":services.smartspace-sources",
":services.speech-sources",
":services.startop.iorap-sources",
":services.systemcaptions-sources",
@@ -77,6 +78,7 @@ java_library {
"services.profcollect",
"services.restrictions",
"services.searchui",
+ "services.smartspace",
"services.speech",
"services.startop",
"services.systemcaptions",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4f0efb4406e7..c091dfa384ca 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -216,8 +216,6 @@ import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.utils.PriorityDump;
-import com.google.android.collect.Lists;
-
import libcore.io.IoUtils;
import java.io.FileDescriptor;
@@ -1577,7 +1575,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public NetworkInfo[] getAllNetworkInfo() {
enforceAccessPermission();
- final ArrayList<NetworkInfo> result = Lists.newArrayList();
+ final ArrayList<NetworkInfo> result = new ArrayList<>();
for (int networkType = 0; networkType <= ConnectivityManager.MAX_NETWORK_TYPE;
networkType++) {
NetworkInfo info = getNetworkInfo(networkType);
@@ -1851,7 +1849,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// This contains IMSI details, so make sure the caller is privileged.
NetworkStack.checkNetworkStackPermission(mContext);
- final ArrayList<NetworkState> result = Lists.newArrayList();
+ final ArrayList<NetworkState> result = new ArrayList<>();
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai != null) {
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 8b506bac4a85..41903fcd165f 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -372,10 +372,12 @@ public class PackageWatchdog {
* even from a previous boot.
*/
public void unregisterHealthObserver(PackageHealthObserver observer) {
- synchronized (mLock) {
- mAllObservers.remove(observer.getName());
- }
- syncState("unregistering observer: " + observer.getName());
+ mLongTaskHandler.post(() -> {
+ synchronized (mLock) {
+ mAllObservers.remove(observer.getName());
+ }
+ syncState("unregistering observer: " + observer.getName());
+ });
}
/**
@@ -982,7 +984,11 @@ public class PackageWatchdog {
if (!DeviceConfig.NAMESPACE_ROLLBACK.equals(properties.getNamespace())) {
return;
}
- updateConfigs();
+ try {
+ updateConfigs();
+ } catch (Exception ignore) {
+ Slog.w(TAG, "Failed to reload device config changes");
+ }
});
}
@@ -990,7 +996,8 @@ public class PackageWatchdog {
* Health check is enabled or disabled after reading the flags
* from DeviceConfig.
*/
- private void updateConfigs() {
+ @VisibleForTesting
+ void updateConfigs() {
synchronized (mLock) {
mTriggerFailureCount = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_ROLLBACK,
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0412f08d3ae3..7d6515600c2a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -48,6 +48,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
@@ -482,14 +483,21 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- private @Nullable VolumeInfo findStorageForUuid(String volumeUuid) {
+ private @Nullable VolumeInfo findStorageForUuidAsUser(String volumeUuid,
+ @UserIdInt int userId) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
- return storage.findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL + ";" + 0);
+ return storage.findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL + ";" + userId);
} else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
return storage.getPrimaryPhysicalVolume();
} else {
- return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid));
+ VolumeInfo info = storage.findVolumeByUuid(volumeUuid);
+ if (info == null) {
+ Slog.w(TAG, "findStorageForUuidAsUser cannot find volumeUuid:" + volumeUuid);
+ return null;
+ }
+ String emulatedUuid = info.getId().replace("private", "emulated") + ";" + userId;
+ return storage.findVolumeById(emulatedUuid);
}
}
@@ -2605,8 +2613,9 @@ class StorageManagerService extends IStorageManager.Stub
return;
} else {
- from = findStorageForUuid(mPrimaryStorageUuid);
- to = findStorageForUuid(volumeUuid);
+ int currentUserId = mCurrentUserId;
+ from = findStorageForUuidAsUser(mPrimaryStorageUuid, currentUserId);
+ to = findStorageForUuidAsUser(volumeUuid, currentUserId);
if (from == null) {
Slog.w(TAG, "Failing move due to missing from volume " + mPrimaryStorageUuid);
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index a08d066513c7..e96fd390f15a 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -32,6 +32,7 @@ import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
+import android.net.NetworkStack;
import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
import android.net.TestNetworkInterface;
@@ -48,6 +49,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.NetdUtils;
import java.io.UncheckedIOException;
import java.net.Inet4Address;
@@ -317,10 +319,10 @@ class TestNetworkService extends ITestNetworkManager.Stub {
}
try {
- // This requires NETWORK_STACK privileges.
final long token = Binder.clearCallingIdentity();
try {
- mNMS.setInterfaceUp(iface);
+ NetworkStack.checkNetworkStackPermission(mContext);
+ NetdUtils.setInterfaceUp(mNetd, iface);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java
index eca1dfaed810..1738c971afa2 100644
--- a/services/core/java/com/android/server/VibratorManagerService.java
+++ b/services/core/java/com/android/server/VibratorManagerService.java
@@ -22,12 +22,20 @@ import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.vibrator.IVibrator;
+import android.os.BatteryStats;
+import android.os.Binder;
import android.os.CombinedVibrationEffect;
+import android.os.ExternalVibration;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IExternalVibratorService;
import android.os.IVibratorManagerService;
+import android.os.IVibratorStateListener;
import android.os.Looper;
+import android.os.PowerManager;
+import android.os.Process;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.Trace;
@@ -40,9 +48,12 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IBatteryStats;
+import com.android.server.vibrator.InputDeviceDelegate;
import com.android.server.vibrator.Vibration;
import com.android.server.vibrator.VibrationScaler;
import com.android.server.vibrator.VibrationSettings;
+import com.android.server.vibrator.VibrationThread;
import com.android.server.vibrator.VibratorController;
import libcore.util.NativeAllocationRegistry;
@@ -51,6 +62,8 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -83,18 +96,31 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ // Used to generate globally unique vibration ids.
+ private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback
+
private final Object mLock = new Object();
private final Context mContext;
+ private final PowerManager.WakeLock mWakeLock;
+ private final IBatteryStats mBatteryStatsService;
private final Handler mHandler;
private final AppOpsManager mAppOps;
private final NativeWrapper mNativeWrapper;
private final int[] mVibratorIds;
private final SparseArray<VibratorController> mVibrators;
+ private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks();
@GuardedBy("mLock")
private final SparseArray<AlwaysOnVibration> mAlwaysOnEffects = new SparseArray<>();
+ @GuardedBy("mLock")
+ private VibrationThread mCurrentVibration;
+ @GuardedBy("mLock")
+ private VibrationThread mNextVibration;
+ @GuardedBy("mLock")
+ private ExternalVibrationHolder mCurrentExternalVibration;
private VibrationSettings mVibrationSettings;
private VibrationScaler mVibrationScaler;
+ private InputDeviceDelegate mInputDeviceDelegate;
static native long nativeInit();
@@ -109,8 +135,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mNativeWrapper = injector.getNativeWrapper();
mNativeWrapper.init();
+ mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+ BatteryStats.SERVICE_NAME));
+
mAppOps = mContext.getSystemService(AppOpsManager.class);
+ PowerManager pm = context.getSystemService(PowerManager.class);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
+ mWakeLock.setReferenceCounted(true);
+
int[] vibratorIds = mNativeWrapper.getVibratorIds();
if (vibratorIds == null) {
mVibratorIds = new int[0];
@@ -134,6 +167,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
try {
mVibrationSettings = new VibrationSettings(mContext, mHandler);
mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
+ mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler);
mVibrationSettings.addListener(this::updateServiceState);
@@ -145,6 +179,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@Override // Binder call
+ public int[] getVibratorIds() {
+ return Arrays.copyOf(mVibratorIds, mVibratorIds.length);
+ }
+
+ @Override // Binder call
@Nullable
public VibratorInfo getVibratorInfo(int vibratorId) {
VibratorController controller = mVibrators.get(vibratorId);
@@ -152,8 +191,37 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@Override // Binder call
- public int[] getVibratorIds() {
- return Arrays.copyOf(mVibratorIds, mVibratorIds.length);
+ public boolean isVibrating(int vibratorId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_VIBRATOR_STATE,
+ "isVibrating");
+ VibratorController controller = mVibrators.get(vibratorId);
+ return controller != null && controller.isVibrating();
+ }
+
+ @Override // Binder call
+ public boolean registerVibratorStateListener(int vibratorId, IVibratorStateListener listener) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_VIBRATOR_STATE,
+ "registerVibratorStateListener");
+ VibratorController controller = mVibrators.get(vibratorId);
+ if (controller == null) {
+ return false;
+ }
+ return controller.registerVibratorStateListener(listener);
+ }
+
+ @Override // Binder call
+ public boolean unregisterVibratorStateListener(int vibratorId,
+ IVibratorStateListener listener) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_VIBRATOR_STATE,
+ "unregisterVibratorStateListener");
+ VibratorController controller = mVibrators.get(vibratorId);
+ if (controller == null) {
+ return false;
+ }
+ return controller.unregisterVibratorStateListener(listener);
}
@Override // Binder call
@@ -161,9 +229,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Nullable CombinedVibrationEffect effect, @Nullable VibrationAttributes attrs) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "setAlwaysOnEffect");
try {
- if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) {
- throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission");
- }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.VIBRATE_ALWAYS_ON,
+ "setAlwaysOnEffect");
+
if (effect == null) {
synchronized (mLock) {
mAlwaysOnEffects.delete(alwaysOnId);
@@ -200,12 +269,89 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Override // Binder call
public void vibrate(int uid, String opPkg, @NonNull CombinedVibrationEffect effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
- throw new UnsupportedOperationException("Not implemented");
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
+ try {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.VIBRATE, "vibrate");
+
+ if (token == null) {
+ Slog.e(TAG, "token must not be null");
+ return;
+ }
+ enforceUpdateAppOpsStatsPermission(uid);
+ if (!isEffectValid(effect)) {
+ return;
+ }
+ effect = fixupVibrationEffect(effect);
+ attrs = fixupVibrationAttributes(attrs);
+ Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), effect, attrs,
+ uid, opPkg, reason);
+
+ synchronized (mLock) {
+ Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib);
+ if (ignoreStatus != null) {
+ endVibrationLocked(vib, ignoreStatus);
+ return;
+ }
+
+ VibrationThread vibThread = new VibrationThread(vib, mVibrators, mWakeLock,
+ mBatteryStatsService, mVibrationCallbacks);
+
+ ignoreStatus = shouldIgnoreVibrationForCurrentLocked(vibThread);
+ if (ignoreStatus != null) {
+ endVibrationLocked(vib, ignoreStatus);
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mCurrentVibration != null) {
+ mCurrentVibration.cancel();
+ }
+ Vibration.Status status = startVibrationLocked(vibThread);
+ if (status != Vibration.Status.RUNNING) {
+ endVibrationLocked(vib, status);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
}
@Override // Binder call
public void cancelVibrate(IBinder token) {
- throw new UnsupportedOperationException("Not implemented");
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "cancelVibrate");
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.VIBRATE,
+ "cancelVibrate");
+
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Canceling vibration.");
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mNextVibration = null;
+ if (mCurrentVibration != null
+ && mCurrentVibration.getVibration().token == token) {
+ mCurrentVibration.cancel();
+ }
+ if (mCurrentExternalVibration != null) {
+ // TODO(b/167946816): end vibration and add to list to be dumped for debug
+ mCurrentExternalVibration.externalVibration.mute();
+ mCurrentExternalVibration = null;
+ // TODO(b/167946816): set external control to false
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
}
@Override
@@ -214,11 +360,24 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
new VibratorManagerShellCommand(this).exec(this, in, out, err, args, cb, resultReceiver);
}
- private void updateServiceState() {
+ @VisibleForTesting
+ void updateServiceState() {
synchronized (mLock) {
+ boolean inputDevicesChanged = mInputDeviceDelegate.updateInputDeviceVibrators(
+ mVibrationSettings.shouldVibrateInputDevices());
+
for (int i = 0; i < mAlwaysOnEffects.size(); i++) {
updateAlwaysOnLocked(mAlwaysOnEffects.valueAt(i));
}
+
+ if (mCurrentVibration == null) {
+ return;
+ }
+
+ if (inputDevicesChanged || !mVibrationSettings.shouldVibrateForPowerMode(
+ mCurrentVibration.getVibration().attrs.getUsage())) {
+ mCurrentVibration.cancel();
+ }
}
}
@@ -230,9 +389,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (vibrator == null) {
continue;
}
- Vibration.Status ignoredStatus = shouldIgnoreVibrationLocked(
+ Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
vib.uid, vib.opPkg, vib.attrs);
- if (ignoredStatus == null) {
+ if (ignoreStatus == null) {
effect = mVibrationScaler.scale(effect, vib.attrs.getUsage());
} else {
// Vibration should not run, use null effect to remove registered effect.
@@ -242,6 +401,134 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ @GuardedBy("mLock")
+ private Vibration.Status startVibrationLocked(VibrationThread vibThread) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
+ try {
+ Vibration vib = vibThread.getVibration();
+ vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage()));
+
+ boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
+ vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs);
+
+ if (inputDevicesAvailable) {
+ return Vibration.Status.FORWARDED_TO_INPUT_DEVICES;
+ }
+
+ if (mCurrentVibration == null) {
+ return startVibrationThreadLocked(vibThread);
+ }
+
+ mNextVibration = vibThread;
+ return Vibration.Status.RUNNING;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private Vibration.Status startVibrationThreadLocked(VibrationThread vibThread) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationThreadLocked");
+ try {
+ Vibration vib = vibThread.getVibration();
+ int mode = startAppOpModeLocked(vib.uid, vib.opPkg, vib.attrs);
+ switch (mode) {
+ case AppOpsManager.MODE_ALLOWED:
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+ mCurrentVibration = vibThread;
+ mCurrentVibration.start();
+ return Vibration.Status.RUNNING;
+ case AppOpsManager.MODE_ERRORED:
+ Slog.w(TAG, "Start AppOpsManager operation errored for uid " + vib.uid);
+ return Vibration.Status.IGNORED_ERROR_APP_OPS;
+ default:
+ return Vibration.Status.IGNORED_APP_OPS;
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void endVibrationLocked(Vibration vib, Vibration.Status status) {
+ // TODO(b/167946816): end vibration and add to list to be dumped for debug
+ }
+
+ @GuardedBy("mLock")
+ private void reportFinishedVibrationLocked(Vibration.Status status) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+ try {
+ Vibration vib = mCurrentVibration.getVibration();
+ mCurrentVibration = null;
+ endVibrationLocked(vib, status);
+ finishAppOpModeLocked(vib.uid, vib.opPkg);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ private void onVibrationComplete(int vibratorId, long vibrationId) {
+ synchronized (mLock) {
+ if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration " + vibrationId + " on vibrator " + vibratorId
+ + " complete, notifying thread");
+ }
+ mCurrentVibration.vibratorComplete(vibratorId);
+ }
+ }
+ }
+
+ /**
+ * Check if given vibration should be ignored in favour of one of the vibrations currently
+ * running on the same vibrators.
+ *
+ * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private Vibration.Status shouldIgnoreVibrationForCurrentLocked(VibrationThread vibThread) {
+ if (vibThread.getVibration().isRepeating()) {
+ // Repeating vibrations always take precedence.
+ return null;
+ }
+ if (mCurrentVibration != null && mCurrentVibration.getVibration().isRepeating()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration in favor of previous alarm vibration");
+ }
+ return Vibration.Status.IGNORED_FOR_ALARM;
+ }
+ return null;
+ }
+
+ /**
+ * Check if given vibration should be ignored by this service.
+ *
+ * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored.
+ * @see #shouldIgnoreVibrationLocked(int, String, VibrationAttributes)
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private Vibration.Status shouldIgnoreVibrationLocked(Vibration vib) {
+ // If something has external control of the vibrator, assume that it's more important.
+ if (mCurrentExternalVibration != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
+ }
+ return Vibration.Status.IGNORED_FOR_EXTERNAL;
+ }
+
+ if (!mVibrationSettings.shouldVibrateForUid(vib.uid, vib.attrs.getUsage())) {
+ Slog.e(TAG, "Ignoring incoming vibration as process with"
+ + " uid= " + vib.uid + " is background,"
+ + " attrs= " + vib.attrs);
+ return Vibration.Status.IGNORED_BACKGROUND;
+ }
+
+ return shouldIgnoreVibrationLocked(vib.uid, vib.opPkg, vib.attrs);
+ }
+
/**
* Check if a vibration with given {@code uid}, {@code opPkg} and {@code attrs} should be
* ignored by this service.
@@ -271,7 +558,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return Vibration.Status.IGNORED_RINGTONE;
}
- int mode = getAppOpMode(uid, opPkg, attrs);
+ int mode = checkAppOpModeLocked(uid, opPkg, attrs);
if (mode != AppOpsManager.MODE_ALLOWED) {
if (mode == AppOpsManager.MODE_ERRORED) {
// We might be getting calls from within system_server, so we don't actually
@@ -290,21 +577,49 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
* Check which mode should be set for a vibration with given {@code uid}, {@code opPkg} and
* {@code attrs}. This will return one of the AppOpsManager.MODE_*.
*/
- private int getAppOpMode(int uid, String opPkg, VibrationAttributes attrs) {
+ @GuardedBy("mLock")
+ private int checkAppOpModeLocked(int uid, String opPkg, VibrationAttributes attrs) {
int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
attrs.getAudioUsage(), uid, opPkg);
- if (mode == AppOpsManager.MODE_ALLOWED) {
- mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, uid, opPkg);
- }
- if (mode == AppOpsManager.MODE_IGNORED
- && attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
+ int fixedMode = fixupAppOpModeLocked(mode, attrs);
+ if (mode != fixedMode && fixedMode == AppOpsManager.MODE_ALLOWED) {
// If we're just ignoring the vibration op then this is set by DND and we should ignore
// if we're asked to bypass. AppOps won't be able to record this operation, so make
// sure we at least note it in the logs for debugging.
Slog.d(TAG, "Bypassing DND for vibrate from uid " + uid);
- mode = AppOpsManager.MODE_ALLOWED;
}
- return mode;
+ return fixedMode;
+ }
+
+ /** Start an operation in {@link AppOpsManager}, if allowed. */
+ @GuardedBy("mLock")
+ private int startAppOpModeLocked(int uid, String opPkg, VibrationAttributes attrs) {
+ return fixupAppOpModeLocked(
+ mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, uid, opPkg), attrs);
+ }
+
+ /**
+ * Finish a previously started operation in {@link AppOpsManager}. This will be a noop if no
+ * operation with same uid was previously started.
+ */
+ @GuardedBy("mLock")
+ private void finishAppOpModeLocked(int uid, String opPkg) {
+ mAppOps.finishOp(AppOpsManager.OP_VIBRATE, uid, opPkg);
+ }
+
+ /**
+ * Enforces {@link android.Manifest.permission#UPDATE_APP_OPS_STATS} to incoming UID if it's
+ * different from the calling UID.
+ */
+ private void enforceUpdateAppOpsStatsPermission(int uid) {
+ if (uid == Binder.getCallingUid()) {
+ return;
+ }
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
}
/**
@@ -330,6 +645,49 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
/**
+ * Sets fallback effects to all prebaked ones in given combination of effects, based on {@link
+ * VibrationSettings#getFallbackEffect}.
+ */
+ private CombinedVibrationEffect fixupVibrationEffect(CombinedVibrationEffect effect) {
+ if (effect instanceof CombinedVibrationEffect.Mono) {
+ return CombinedVibrationEffect.createSynced(
+ fixupVibrationEffect(((CombinedVibrationEffect.Mono) effect).getEffect()));
+ } else if (effect instanceof CombinedVibrationEffect.Stereo) {
+ CombinedVibrationEffect.SyncedCombination combination =
+ CombinedVibrationEffect.startSynced();
+ SparseArray<VibrationEffect> effects =
+ ((CombinedVibrationEffect.Stereo) effect).getEffects();
+ for (int i = 0; i < effects.size(); i++) {
+ combination.addVibrator(effects.keyAt(i), fixupVibrationEffect(effects.valueAt(i)));
+ }
+ return combination.combine();
+ } else if (effect instanceof CombinedVibrationEffect.Sequential) {
+ CombinedVibrationEffect.SequentialCombination combination =
+ CombinedVibrationEffect.startSequential();
+ List<CombinedVibrationEffect> effects =
+ ((CombinedVibrationEffect.Sequential) effect).getEffects();
+ for (CombinedVibrationEffect e : effects) {
+ combination.addNext(fixupVibrationEffect(e));
+ }
+ return combination.combine();
+ }
+ return effect;
+ }
+
+ private VibrationEffect fixupVibrationEffect(VibrationEffect effect) {
+ if (effect instanceof VibrationEffect.Prebaked
+ && ((VibrationEffect.Prebaked) effect).shouldFallback()) {
+ VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
+ VibrationEffect fallback = mVibrationSettings.getFallbackEffect(prebaked.getId());
+ if (fallback != null) {
+ return new VibrationEffect.Prebaked(prebaked.getId(), prebaked.getEffectStrength(),
+ fallback);
+ }
+ }
+ return effect;
+ }
+
+ /**
* Return new {@link VibrationAttributes} that only applies flags that this user has permissions
* to use.
*/
@@ -388,6 +746,19 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ /**
+ * Check given mode, one of the AppOpsManager.MODE_*, against {@link VibrationAttributes} to
+ * allow bypassing {@link AppOpsManager} checks.
+ */
+ @GuardedBy("mLock")
+ private int fixupAppOpModeLocked(int mode, VibrationAttributes attrs) {
+ if (mode == AppOpsManager.MODE_IGNORED
+ && attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
+ return AppOpsManager.MODE_ALLOWED;
+ }
+ return mode;
+ }
+
private boolean hasPermission(String permission) {
return mContext.checkCallingOrSelfPermission(permission)
== PackageManager.PERMISSION_GRANTED;
@@ -428,6 +799,42 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
/**
+ * Implementation of {@link VibrationThread.VibrationCallbacks} that controls synced vibrations
+ * and reports them when finished.
+ */
+ private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {
+
+ @Override
+ public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) {
+ // TODO(b/167946816): call IVibratorManager to prepare
+ }
+
+ @Override
+ public void triggerSyncedVibration(long vibrationId) {
+ // TODO(b/167946816): call IVibratorManager to trigger
+ }
+
+ @Override
+ public void onVibrationEnded(long vibrationId, Vibration.Status status) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration " + vibrationId + " thread finished with status " + status);
+ }
+ synchronized (mLock) {
+ if (mCurrentVibration != null
+ && mCurrentVibration.getVibration().id == vibrationId) {
+ reportFinishedVibrationLocked(status);
+
+ if (mNextVibration != null) {
+ VibrationThread vibThread = mNextVibration;
+ mNextVibration = null;
+ startVibrationThreadLocked(vibThread);
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Implementation of {@link VibratorController.OnVibrationCompleteListener} with a weak
* reference to this service.
*/
@@ -443,7 +850,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
public void onComplete(int vibratorId, long vibrationId) {
VibratorManagerService service = mServiceRef.get();
if (service != null) {
- // TODO(b/159207608): finish vibration if all vibrators finished for this vibration
+ service.onVibrationComplete(vibratorId, vibrationId);
}
}
}
@@ -469,6 +876,41 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ /** Holder for a {@link ExternalVibration}. */
+ private final class ExternalVibrationHolder {
+
+ public final ExternalVibration externalVibration;
+ public int scale;
+
+ private final long mStartTimeDebug;
+ private long mEndTimeDebug;
+ private Vibration.Status mStatus;
+
+ private ExternalVibrationHolder(ExternalVibration externalVibration) {
+ this.externalVibration = externalVibration;
+ this.scale = IExternalVibratorService.SCALE_NONE;
+ mStartTimeDebug = System.currentTimeMillis();
+ mStatus = Vibration.Status.RUNNING;
+ }
+
+ public void end(Vibration.Status status) {
+ if (mStatus != Vibration.Status.RUNNING) {
+ // Vibration already ended, keep first ending status set and ignore this one.
+ return;
+ }
+ mStatus = status;
+ mEndTimeDebug = System.currentTimeMillis();
+ }
+
+ public Vibration.DebugInfo getDebugInfo() {
+ return new Vibration.DebugInfo(
+ mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null,
+ scale, externalVibration.getVibrationAttributes(),
+ externalVibration.getUid(), externalVibration.getPackage(),
+ /* reason= */ null, mStatus);
+ }
+ }
+
/** Wrapper around the static-native methods of {@link VibratorManagerService} for tests. */
@VisibleForTesting
public static class NativeWrapper {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index d63a6c3a0a6f..5b5043141315 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -111,7 +111,6 @@ public class Watchdog {
};
public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
- "android.hardware.audio@2.0::IDevicesFactory",
"android.hardware.audio@4.0::IDevicesFactory",
"android.hardware.audio@5.0::IDevicesFactory",
"android.hardware.audio@6.0::IDevicesFactory",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0c8172d204c4..6216fc00417a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2815,12 +2815,24 @@ public final class ActiveServices {
r = smap.mServicesByIntent.get(filter);
if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by intent: " + r);
}
- if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
- && !callingPackage.equals(r.packageName)) {
- // If an external service is running within its own package, other packages
- // should not bind to that instance.
- r = null;
- if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Whoops, can't use existing external service");
+ if (r != null) {
+ // Compared to resolveService below, the ServiceRecord here is retrieved from
+ // ServiceMap so the package visibility doesn't apply to it. We need to filter it.
+ if (mAm.getPackageManagerInternal().filterAppAccess(r.packageName, callingUid,
+ userId)) {
+ Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId
+ + ": not found");
+ return null;
+ }
+ if ((r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
+ && !callingPackage.equals(r.packageName)) {
+ // If an external service is running within its own package, other packages
+ // should not bind to that instance.
+ r = null;
+ if (DEBUG_SERVICE) {
+ Slog.v(TAG_SERVICE, "Whoops, can't use existing external service");
+ }
+ }
}
if (r == null) {
try {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 52f64f2ca6e1..c8f5f8e60187 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -236,6 +236,7 @@ import android.os.DropBoxManager;
import android.os.FactoryTest;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IDeviceIdentifiersPolicyService;
import android.os.IPermissionController;
@@ -2098,11 +2099,19 @@ public class ActivityManagerService extends IActivityManager.Stub
mEnableOffloadQueue = SystemProperties.getBoolean(
"persist.device_config.activity_manager_native_boot.offload_queue_enabled", false);
- mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
+ // Decouple broadcast-related timing operations from other OS activity by
+ // using a dedicated thread. Sharing this thread between queues is safe
+ // because we know the nature of the activity on it and can't stall
+ // unexpectedly.
+ HandlerThread broadcastThread = new HandlerThread("broadcast");
+ broadcastThread.start();
+ Handler broadcastHandler = broadcastThread.getThreadHandler();
+
+ mFgBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
"foreground", foreConstants, false);
- mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mBgBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
"background", backConstants, true);
- mOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mOffloadBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
"offload", offloadConstants, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
@@ -4500,7 +4509,6 @@ public class ActivityManagerService extends IActivityManager.Stub
String data, Bundle extras, boolean ordered,
boolean sticky, int sendingUser) {
synchronized (ActivityManagerService.this) {
- mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
mAppProfiler.requestPssAllProcsLocked(
SystemClock.uptimeMillis(), true, false);
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 27a238dd33ea..c558b3d00400 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -741,10 +741,12 @@ public final class CachedAppOptimizer {
// This will ensure app will be out of the freezer for at least FREEZE_TIMEOUT_MS
void unfreezeTemporarily(ProcessRecord app) {
- synchronized (mAm) {
- if (app.frozen) {
- unfreezeAppLocked(app);
- freezeAppAsync(app);
+ if (mUseFreezer) {
+ synchronized (mAm) {
+ if (app.frozen) {
+ unfreezeAppLocked(app);
+ freezeAppAsync(app);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a768532a9260..bbf927b1ee2d 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -4447,7 +4447,7 @@ public final class ProcessList {
}
app.getPkgList().forEachPackage(packageName -> {
- if (updateFrameworkRes && packagesToUpdate.contains(packageName)) {
+ if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
try {
final ApplicationInfo ai = AppGlobals.getPackageManager()
.getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index cf309f4c8e3d..9fd2bd798c04 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -623,6 +623,8 @@ final class ProcessProfileRecord {
DebugUtils.printSizeValue(pw, mLastCachedSwapPss * 1024);
pw.print(" lastRss=");
DebugUtils.printSizeValue(pw, mLastRss * 1024);
+ pw.println();
+ pw.print(prefix);
pw.print(" trimMemoryLevel=");
pw.println(mTrimMemoryLevel);
pw.println();
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index b2824846008c..1a4f20c7101e 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -57,6 +57,7 @@ import com.android.internal.util.WakeupMessage;
import com.android.server.ConnectivityService;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
@@ -1025,6 +1026,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
+ (networkAgentConfig.acceptUnvalidated ? " acceptUnvalidated" : "")
+ (networkAgentConfig.acceptPartialConnectivity ? " acceptPartialConnectivity" : "")
+ (clatd.isStarted() ? " clat{" + clatd + "} " : "")
+ + (declaredUnderlyingNetworks != null
+ ? " underlying{" + Arrays.toString(declaredUnderlyingNetworks) + "}" : "")
+ " lp{" + linkProperties + "}"
+ " nc{" + networkCapabilities + "}"
+ "}";
diff --git a/services/core/java/com/android/server/graphics/fonts/FontCrashDetector.java b/services/core/java/com/android/server/graphics/fonts/FontCrashDetector.java
new file mode 100644
index 000000000000..b082b25aea02
--- /dev/null
+++ b/services/core/java/com/android/server/graphics/fonts/FontCrashDetector.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.graphics.fonts;
+
+import android.annotation.NonNull;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A class to detect font-related native crash.
+ *
+ * <p>If a fs-verity protected file is accessed through mmap and corrupted file block is detected,
+ * SIGBUG signal is generated and the process will crash. To find corrupted files and remove them,
+ * we use a marker file to detect crash.
+ * <ol>
+ * <li>Create a marker file before reading fs-verity protected font files.
+ * <li>Delete the marker file after reading font files successfully.
+ * <li>If the marker file is found in the next process startup, it means that the process
+ * crashed before. We will delete font files to prevent crash loop.
+ * </ol>
+ *
+ * <p>Example usage:
+ * <pre>
+ * FontCrashDetector detector = new FontCrashDetector(new File("/path/to/marker_file"));
+ * if (detector.hasCrashed()) {
+ * // Do cleanup
+ * }
+ * try (FontCrashDetector.MonitoredBlock b = detector.start()) {
+ * // Read files
+ * }
+ * </pre>
+ *
+ * <p>This class DOES NOT detect Java exceptions. If a Java exception is thrown while monitoring
+ * crash, the marker file will be deleted. Creating and deleting marker files are not lightweight.
+ * Please use this class sparingly with caution.
+ */
+/* package */ final class FontCrashDetector {
+
+ private static final String TAG = "FontCrashDetector";
+
+ @NonNull
+ private final File mMarkerFile;
+
+ /* package */ FontCrashDetector(@NonNull File markerFile) {
+ mMarkerFile = markerFile;
+ }
+
+ /* package */ boolean hasCrashed() {
+ return mMarkerFile.exists();
+ }
+
+ /* package */ void clear() {
+ if (!mMarkerFile.delete()) {
+ Slog.e(TAG, "Could not delete marker file: " + mMarkerFile);
+ }
+ }
+
+ /** Starts crash monitoring. */
+ /* package */ MonitoredBlock start() {
+ try {
+ mMarkerFile.createNewFile();
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not create marker file: " + mMarkerFile, e);
+ }
+ return new MonitoredBlock();
+ }
+
+ /** A helper class to monitor crash with try-with-resources syntax. */
+ /* package */ class MonitoredBlock implements AutoCloseable {
+ /** Ends crash monitoring. */
+ @Override
+ public void close() {
+ clear();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 7461405c9a5f..8e5215b49f16 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -64,6 +64,7 @@ public final class FontManagerService extends IFontManager.Stub {
private static final String TAG = "FontManagerService";
private static final String FONT_FILES_DIR = "/data/fonts/files";
+ private static final String CRASH_MARKER_FILE = "/data/fonts/config/crash.txt";
@Override
public FontConfig getFontConfig() throws RemoteException {
@@ -179,6 +180,13 @@ public final class FontManagerService extends IFontManager.Stub {
}
}
+ @NonNull
+ private final Context mContext;
+
+ @GuardedBy("FontManagerService.this")
+ @NonNull
+ private final FontCrashDetector mFontCrashDetector;
+
@Nullable
private final UpdatableFontDir mUpdatableFontDir;
@@ -188,7 +196,9 @@ public final class FontManagerService extends IFontManager.Stub {
private FontManagerService(Context context) {
mContext = context;
+ mFontCrashDetector = new FontCrashDetector(new File(CRASH_MARKER_FILE));
mUpdatableFontDir = createUpdatableFontDir();
+ initialize();
}
@Nullable
@@ -201,20 +211,35 @@ public final class FontManagerService extends IFontManager.Stub {
new OtfFontFileParser(), new FsverityUtilImpl());
}
-
- @NonNull
- private final Context mContext;
+ private void initialize() {
+ synchronized (FontManagerService.this) {
+ if (mUpdatableFontDir == null) {
+ mSerializedFontMap = buildNewSerializedFontMap();
+ return;
+ }
+ if (mFontCrashDetector.hasCrashed()) {
+ Slog.i(TAG, "Crash detected. Clearing font updates.");
+ try {
+ mUpdatableFontDir.clearUpdates();
+ } catch (SystemFontException e) {
+ Slog.e(TAG, "Failed to clear updates.", e);
+ }
+ mFontCrashDetector.clear();
+ }
+ try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
+ mUpdatableFontDir.loadFontFileMap();
+ mSerializedFontMap = buildNewSerializedFontMap();
+ }
+ }
+ }
@NonNull
public Context getContext() {
return mContext;
}
- @NonNull /* package */ SharedMemory getCurrentFontMap() {
+ @Nullable /* package */ SharedMemory getCurrentFontMap() {
synchronized (FontManagerService.this) {
- if (mSerializedFontMap == null) {
- mSerializedFontMap = buildNewSerializedFontMap();
- }
return mSerializedFontMap;
}
}
@@ -234,9 +259,10 @@ public final class FontManagerService extends IFontManager.Stub {
FontManager.RESULT_ERROR_VERSION_MISMATCH,
"The base config version is older than current.");
}
- mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
- // Create updated font map in the next getSerializedSystemFontMap() call.
- mSerializedFontMap = null;
+ try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
+ mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
+ mSerializedFontMap = buildNewSerializedFontMap();
+ }
}
}
@@ -246,7 +272,12 @@ public final class FontManagerService extends IFontManager.Stub {
FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
"The font updater is disabled.");
}
- mUpdatableFontDir.clearUpdates();
+ synchronized (FontManagerService.this) {
+ try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
+ mUpdatableFontDir.clearUpdates();
+ mSerializedFontMap = buildNewSerializedFontMap();
+ }
+ }
}
/* package */ Map<String, File> getFontFileMap() {
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index b0bc65bc7787..0cb704507f7a 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -142,11 +142,9 @@ final class UpdatableFontDir {
mFsverityUtil = fsverityUtil;
mConfigFile = configFile;
mTmpConfigFile = new File(configFile.getAbsoluteFile() + ".tmp");
- loadFontFileMap();
}
- private void loadFontFileMap() {
- // TODO: SIGBUS crash protection
+ /* package */ void loadFontFileMap() {
synchronized (UpdatableFontDir.this) {
boolean success = false;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index f6e08fbaf379..2e4200c1f7d9 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -289,6 +289,8 @@ public class InputManagerService extends IInputManager.Stub
private static native void nativeCancelVibrate(long ptr, int deviceId, int token);
private static native boolean nativeIsVibrating(long ptr, int deviceId);
private static native int[] nativeGetVibratorIds(long ptr, int deviceId);
+ private static native int nativeGetBatteryCapacity(long ptr, int deviceId);
+ private static native int nativeGetBatteryStatus(long ptr, int deviceId);
private static native void nativeReloadKeyboardLayouts(long ptr);
private static native void nativeReloadDeviceAliases(long ptr);
private static native String nativeDump(long ptr);
@@ -2008,6 +2010,18 @@ public class InputManagerService extends IInputManager.Stub
// Binder call
@Override
+ public int getBatteryStatus(int deviceId) {
+ return nativeGetBatteryStatus(mPtr, deviceId);
+ }
+
+ // Binder call
+ @Override
+ public int getBatteryCapacity(int deviceId) {
+ return nativeGetBatteryCapacity(mPtr, deviceId);
+ }
+
+ // Binder call
+ @Override
public void setPointerIconType(int iconId) {
nativeSetPointerIconType(mPtr, iconId);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6308ace26011..e5b53501d6e3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4131,48 +4131,52 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
@GuardedBy("mMethodMap")
- public void startProtoDump(byte[] protoDump, int source, String where) {
- if (protoDump == null && source != IME_TRACING_FROM_IMMS) {
- // Dump not triggered from IMMS, but no proto information provided.
- return;
- }
- ImeTracing tracingInstance = ImeTracing.getInstance();
- if (!tracingInstance.isAvailable() || !tracingInstance.isEnabled()) {
- return;
- }
-
- ProtoOutputStream proto = new ProtoOutputStream();
- switch (source) {
- case ImeTracing.IME_TRACING_FROM_CLIENT:
- final long client_token = proto.start(InputMethodClientsTraceFileProto.ENTRY);
- proto.write(InputMethodClientsTraceProto.ELAPSED_REALTIME_NANOS,
- SystemClock.elapsedRealtimeNanos());
- proto.write(InputMethodClientsTraceProto.WHERE, where);
- proto.write(InputMethodClientsTraceProto.CLIENT, protoDump);
- proto.end(client_token);
- break;
- case ImeTracing.IME_TRACING_FROM_IMS:
- final long service_token = proto.start(InputMethodServiceTraceFileProto.ENTRY);
- proto.write(InputMethodServiceTraceProto.ELAPSED_REALTIME_NANOS,
- SystemClock.elapsedRealtimeNanos());
- proto.write(InputMethodServiceTraceProto.WHERE, where);
- proto.write(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE, protoDump);
- proto.end(service_token);
- break;
- case IME_TRACING_FROM_IMMS:
- final long managerservice_token =
- proto.start(InputMethodManagerServiceTraceFileProto.ENTRY);
- proto.write(InputMethodManagerServiceTraceProto.ELAPSED_REALTIME_NANOS,
- SystemClock.elapsedRealtimeNanos());
- proto.write(InputMethodManagerServiceTraceProto.WHERE, where);
- dumpDebug(proto, InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE);
- proto.end(managerservice_token);
- break;
- default:
- // Dump triggered by a source not recognised.
+ public void startProtoDump(byte[] protoDump, int source, String where,
+ IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> {
+ if (protoDump == null && source != IME_TRACING_FROM_IMMS) {
+ // Dump not triggered from IMMS, but no proto information provided.
return;
- }
- tracingInstance.addToBuffer(proto, source);
+ }
+ ImeTracing tracingInstance = ImeTracing.getInstance();
+ if (!tracingInstance.isAvailable() || !tracingInstance.isEnabled()) {
+ return;
+ }
+
+ ProtoOutputStream proto = new ProtoOutputStream();
+ switch (source) {
+ case ImeTracing.IME_TRACING_FROM_CLIENT:
+ final long client_token = proto.start(InputMethodClientsTraceFileProto.ENTRY);
+ proto.write(InputMethodClientsTraceProto.ELAPSED_REALTIME_NANOS,
+ SystemClock.elapsedRealtimeNanos());
+ proto.write(InputMethodClientsTraceProto.WHERE, where);
+ proto.write(InputMethodClientsTraceProto.CLIENT, protoDump);
+ proto.end(client_token);
+ break;
+ case ImeTracing.IME_TRACING_FROM_IMS:
+ final long service_token = proto.start(InputMethodServiceTraceFileProto.ENTRY);
+ proto.write(InputMethodServiceTraceProto.ELAPSED_REALTIME_NANOS,
+ SystemClock.elapsedRealtimeNanos());
+ proto.write(InputMethodServiceTraceProto.WHERE, where);
+ proto.write(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE, protoDump);
+ proto.end(service_token);
+ break;
+ case IME_TRACING_FROM_IMMS:
+ final long managerservice_token =
+ proto.start(InputMethodManagerServiceTraceFileProto.ENTRY);
+ proto.write(InputMethodManagerServiceTraceProto.ELAPSED_REALTIME_NANOS,
+ SystemClock.elapsedRealtimeNanos());
+ proto.write(InputMethodManagerServiceTraceProto.WHERE, where);
+ dumpDebug(proto,
+ InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE);
+ proto.end(managerservice_token);
+ break;
+ default:
+ // Dump triggered by a source not recognised.
+ return;
+ }
+ tracingInstance.addToBuffer(proto, source);
+ });
}
@BinderThread
@@ -4183,40 +4187,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
- public void startImeTrace() {
- ImeTracing.getInstance().startTrace(null /* printwriter */);
- ArrayMap<IBinder, ClientState> clients;
- synchronized (mMethodMap) {
- clients = new ArrayMap<>(mClients);
- }
- for (ClientState state : clients.values()) {
- if (state != null) {
- try {
- state.client.setImeTraceEnabled(true /* enabled */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error while trying to enable ime trace on client window", e);
+ public void startImeTrace(IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> {
+ ImeTracing.getInstance().startTrace(null /* printwriter */);
+ ArrayMap<IBinder, ClientState> clients;
+ synchronized (mMethodMap) {
+ clients = new ArrayMap<>(mClients);
+ }
+ for (ClientState state : clients.values()) {
+ if (state != null) {
+ try {
+ state.client.setImeTraceEnabled(true /* enabled */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error while trying to enable ime trace on client window", e);
+ }
}
}
- }
+ });
}
@BinderThread
@Override
- public void stopImeTrace() {
- ImeTracing.getInstance().stopTrace(null /* printwriter */);
- ArrayMap<IBinder, ClientState> clients;
- synchronized (mMethodMap) {
- clients = new ArrayMap<>(mClients);
- }
- for (ClientState state : clients.values()) {
- if (state != null) {
- try {
- state.client.setImeTraceEnabled(false /* enabled */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error while trying to disable ime trace on client window", e);
+ public void stopImeTrace(IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> {
+ ImeTracing.getInstance().stopTrace(null /* printwriter */);
+ ArrayMap<IBinder, ClientState> clients;
+ synchronized (mMethodMap) {
+ clients = new ArrayMap<>(mClients);
+ }
+ for (ClientState state : clients.values()) {
+ if (state != null) {
+ try {
+ state.client.setImeTraceEnabled(false /* enabled */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error while trying to disable ime trace on client window", e);
+ }
}
}
- }
+ });
}
@GuardedBy("mMethodMap")
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 7f9c76634422..6fec9063ba94 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1868,7 +1868,9 @@ public final class MultiClientInputMethodManagerService {
@BinderThread
@Override
- public void startProtoDump(byte[] clientProtoDump, int source, String where) {
+ public void startProtoDump(byte[] clientProtoDump, int source, String where,
+ IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> { });
}
@BinderThread
@@ -1879,12 +1881,14 @@ public final class MultiClientInputMethodManagerService {
@BinderThread
@Override
- public void startImeTrace() {
+ public void startImeTrace(IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> { });
}
@BinderThread
@Override
- public void stopImeTrace() {
+ public void stopImeTrace(IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> { });
}
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index dc1a26ae6fea..785e6745087f 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -16,6 +16,7 @@
package com.android.server.location.contexthub;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -320,6 +321,10 @@ public class ContextHubService extends IContextHubService.Stub {
@Override
public void onNanoAppDisabled(long nanoAppId) {
}
+
+ @Override
+ public void onClientAuthorizationChanged(long nanoAppId, int authorization) {
+ }
};
}
@@ -697,6 +702,7 @@ public class ContextHubService extends IContextHubService.Stub {
*
* @param contextHubId the ID of the hub this client is attached to
* @param clientCallback the client interface to register with the service
+ * @param attributionTag an optional attribution tag within the given package
* @return the generated client interface, null if registration was unsuccessful
* @throws IllegalArgumentException if contextHubId is not a valid ID
* @throws IllegalStateException if max number of clients have already registered
@@ -704,7 +710,8 @@ public class ContextHubService extends IContextHubService.Stub {
*/
@Override
public IContextHubClient createClient(
- int contextHubId, IContextHubClientCallback clientCallback) throws RemoteException {
+ int contextHubId, IContextHubClientCallback clientCallback,
+ @Nullable String attributionTag) throws RemoteException {
checkPermissions();
if (!isValidContextHubId(contextHubId)) {
throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
@@ -723,13 +730,15 @@ public class ContextHubService extends IContextHubService.Stub {
* @param contextHubId the ID of the hub this client is attached to
* @param pendingIntent the PendingIntent associated with this client
* @param nanoAppId the ID of the nanoapp PendingIntent events will be sent for
+ * @param attributionTag an optional attribution tag within the given package
* @return the generated client interface
* @throws IllegalArgumentException if hubInfo does not represent a valid hub
* @throws IllegalStateException if there were too many registered clients at the service
*/
@Override
public IContextHubClient createPendingIntentClient(
- int contextHubId, PendingIntent pendingIntent, long nanoAppId) throws RemoteException {
+ int contextHubId, PendingIntent pendingIntent, long nanoAppId,
+ @Nullable String attributionTag) throws RemoteException {
checkPermissions();
if (!isValidContextHubId(contextHubId)) {
throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e32c00fe9a39..6843733eea9f 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -377,7 +377,8 @@ public class NotificationManagerService extends SystemService {
static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] {
Adjustment.KEY_CONTEXTUAL_ACTIONS,
- Adjustment.KEY_TEXT_REPLIES};
+ Adjustment.KEY_TEXT_REPLIES,
+ Adjustment.KEY_NOT_CONVERSATION};
static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] {
RoleManager.ROLE_DIALER,
@@ -2313,6 +2314,13 @@ public class NotificationManagerService extends SystemService {
} else if ("false".equals(value)) {
mAssistants.disallowAdjustmentType(Adjustment.KEY_RANKING_SCORE);
}
+ } else if (SystemUiDeviceConfigFlags.ENABLE_NAS_NOT_CONVERSATION.equals(name)) {
+ String value = properties.getString(name, null);
+ if ("true".equals(value)) {
+ mAssistants.allowAdjustmentType(Adjustment.KEY_NOT_CONVERSATION);
+ } else if ("false".equals(value)) {
+ mAssistants.disallowAdjustmentType(Adjustment.KEY_NOT_CONVERSATION);
+ }
}
}
};
@@ -9302,21 +9310,30 @@ public class NotificationManagerService extends SystemService {
Slog.v(TAG, "onNotificationEnqueuedLocked() called with: r = [" + r + "]");
}
final StatusBarNotification sbn = r.getSbn();
- notifyAssistantLocked(
- sbn,
- r.getNotificationType(),
- true /* sameUserOnly */,
- (assistant, sbnHolder) -> {
- try {
- if (debug) {
- Slog.v(TAG,
- "calling onNotificationEnqueuedWithChannel " + sbnHolder);
- }
- assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel());
- } catch (RemoteException ex) {
- Slog.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+
+ for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+ boolean sbnVisible = isVisibleToListener(
+ sbn, r.getNotificationType(), info)
+ && info.isSameUser(r.getUserId());
+ if (sbnVisible) {
+ TrimCache trimCache = new TrimCache(sbn);
+ final INotificationListener assistant = (INotificationListener) info.service;
+ final StatusBarNotification sbnToPost = trimCache.ForListener(info);
+ final StatusBarNotificationHolder sbnHolder =
+ new StatusBarNotificationHolder(sbnToPost);
+ try {
+ if (debug) {
+ Slog.v(TAG,
+ "calling onNotificationEnqueuedWithChannel " + sbnHolder);
}
- });
+ final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
+ assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel(),
+ update);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+ }
+ }
+ }
}
@GuardedBy("mNotificationLock")
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index f59934fd6810..d7bc3bb8af28 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -523,27 +523,39 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
if (VERBOSE) Slog.i(TAG, "Executing: validation for: " + mKey);
long timeStartMs = System.currentTimeMillis();
for (final String handle: mPendingLookups) {
+ final String cacheKey = getCacheKey(mContext.getUserId(), handle);
LookupResult lookupResult = null;
- final Uri uri = Uri.parse(handle);
- if ("tel".equals(uri.getScheme())) {
- if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
- lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
- } else if ("mailto".equals(uri.getScheme())) {
- if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
- lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
- } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
- if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
- lookupResult = searchContacts(mContext, uri);
- } else {
- lookupResult = new LookupResult(); // invalid person for the cache
- if (!"name".equals(uri.getScheme())) {
- Slog.w(TAG, "unsupported URI " + handle);
+ boolean cacheHit = false;
+ synchronized (mPeopleCache) {
+ lookupResult = mPeopleCache.get(cacheKey);
+ if (lookupResult != null && !lookupResult.isExpired()) {
+ // The name wasn't already added to the cache, no need to retry
+ cacheHit = true;
+ }
+ }
+ if (!cacheHit) {
+ final Uri uri = Uri.parse(handle);
+ if ("tel".equals(uri.getScheme())) {
+ if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
+ lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
+ } else if ("mailto".equals(uri.getScheme())) {
+ if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
+ lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
+ } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
+ if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
+ lookupResult = searchContacts(mContext, uri);
+ } else {
+ lookupResult = new LookupResult(); // invalid person for the cache
+ if (!"name".equals(uri.getScheme())) {
+ Slog.w(TAG, "unsupported URI " + handle);
+ }
}
}
if (lookupResult != null) {
- synchronized (mPeopleCache) {
- final String cacheKey = getCacheKey(mContext.getUserId(), handle);
- mPeopleCache.put(cacheKey, lookupResult);
+ if (!cacheHit) {
+ synchronized (mPeopleCache) {
+ mPeopleCache.put(cacheKey, lookupResult);
+ }
}
if (DEBUG) {
Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());
@@ -580,4 +592,3 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
}
}
}
-
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index e0b57e4ce42f..402f6467e82d 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -118,8 +118,8 @@ public class BackgroundDexOptService extends JobService {
// Schedule a one-off job which scans installed packages and updates
// out-of-date oat files.
js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
- .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
- .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
+ .setMinimumLatency(TimeUnit.MINUTES.toMillis(10))
+ .setOverrideDeadline(TimeUnit.MINUTES.toMillis(60))
.build());
// Schedule a daily job which scans installed packages and compiles
diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
index da65fe2bc0ab..c65c2b112706 100644
--- a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
+++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
@@ -234,7 +234,7 @@ public class DynamicCodeLoggingService extends JobService {
List<EventLog.Event> events = new ArrayList<>();
EventLog.readEvents(tags, events);
-
+ Matcher matcher = EXECUTE_NATIVE_AUDIT_PATTERN.matcher("");
for (int i = 0; i < events.size(); ++i) {
if (mAuditWatchingStopRequested) {
Log.w(TAG, "Stopping AuditWatchingJob run at scheduler request");
@@ -259,7 +259,9 @@ public class DynamicCodeLoggingService extends JobService {
// And then use a regular expression to verify it's one of the messages we're
// interested in and to extract the path of the file being loaded.
- Matcher matcher = EXECUTE_NATIVE_AUDIT_PATTERN.matcher(message);
+ // Reuse the Matcher to avoid unnecessary string garbage caused by libcore's
+ // regex matching.
+ matcher.reset(message);
if (!matcher.matches()) {
continue;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4ef892d9fcba..f68113d5520a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18819,9 +18819,11 @@ public class PackageManagerService extends IPackageManager.Stub
final VersionInfo versionInfo = request.versionInfos.get(installPackageName);
final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
+ final boolean isRollback = installArgs != null
+ && installArgs.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
final boolean compatMatch = verifySignatures(signatureCheckPs,
disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
- compareRecover);
+ compareRecover, isRollback);
// The new KeySets will be re-added later in the scanning process.
if (compatMatch) {
removeAppKeySetData = true;
@@ -19791,6 +19793,7 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
final boolean virtualPreload =
((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+ final boolean isRollback = args.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
@ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (args.move != null) {
// moving a complete application; perform an initial scan on the new install location
@@ -19971,7 +19974,8 @@ public class PackageManagerService extends IPackageManager.Stub
parsedPackage);
// We don't care about disabledPkgSetting on install for now.
final boolean compatMatch = verifySignatures(signatureCheckPs, null,
- parsedPackage.getSigningDetails(), compareCompat, compareRecover);
+ parsedPackage.getSigningDetails(), compareCompat, compareRecover,
+ isRollback);
// The new KeySets will be re-added later in the scanning process.
if (compatMatch) {
synchronized (mLock) {
@@ -20248,15 +20252,23 @@ public class PackageManagerService extends IPackageManager.Stub
+ pkgName11);
}
} else {
+ SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails();
+ SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails();
// default to original signature matching
- if (!parsedPackage.getSigningDetails().checkCapability(
- oldPackage.getSigningDetails(),
+ if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails,
SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !oldPackage.getSigningDetails().checkCapability(
- parsedPackage.getSigningDetails(),
+ && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails,
SigningDetails.CertCapabilities.ROLLBACK)) {
- throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "New package has a different signature: " + pkgName11);
+ // Allow the update to proceed if this is a rollback and the parsed
+ // package's current signing key is the current signer or in the lineage
+ // of the old package; this allows a rollback to a previously installed
+ // version after an app's signing key has been rotated without requiring
+ // the rollback capability on the previous signing key.
+ if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf(
+ parsedPkgSigningDetails)) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "New package has a different signature: " + pkgName11);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index ee94b8599625..8015063812e6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -624,7 +624,7 @@ public class PackageManagerServiceUtils {
*/
public static boolean verifySignatures(PackageSetting pkgSetting,
PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
- boolean compareCompat, boolean compareRecover)
+ boolean compareCompat, boolean compareRecover, boolean isRollback)
throws PackageManagerException {
final String packageName = pkgSetting.name;
boolean compatMatch = false;
@@ -658,6 +658,13 @@ public class PackageManagerServiceUtils {
match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);
}
+ if (!match && isRollback) {
+ // Since a rollback can only be initiated for an APK previously installed on the
+ // device allow rolling back to a previous signing key even if the rollback
+ // capability has not been granted.
+ match = pkgSetting.signatures.mSigningDetails.hasAncestorOrSelf(parsedSignatures);
+ }
+
if (!match) {
throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"Package " + packageName +
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index e12991a220c9..9560f59924de 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -563,6 +563,7 @@ class Rollback {
params.setRequestDowngrade(true);
params.setRequiredInstalledVersionCode(
pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode());
+ params.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
if (isStaged()) {
params.setStaged();
}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 9d21b9241c0d..132883e4a041 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -168,8 +168,8 @@ public class Vcn extends Handler {
@NonNull NetworkRequest request, int score, int providerId) {
if (score > getNetworkScore()) {
Slog.v(getLogTag(),
- "Request " + request.requestId + " already satisfied by higher-scoring ("
- + score + ") network from provider " + providerId);
+ "Request already satisfied by higher-scoring (" + score + ") network from "
+ + "provider " + providerId + ": " + request);
return;
}
@@ -177,8 +177,7 @@ public class Vcn extends Handler {
for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
Slog.v(getLogTag(),
- "Request " + request.requestId
- + " satisfied by existing VcnGatewayConnection");
+ "Request already satisfied by existing VcnGatewayConnection: " + request);
return;
}
}
@@ -202,12 +201,12 @@ public class Vcn extends Handler {
private boolean requestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
- final NetworkCapabilities configCaps = new NetworkCapabilities();
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
for (int cap : config.getAllExposedCapabilities()) {
- configCaps.addCapability(cap);
+ builder.addCapability(cap);
}
- return request.networkCapabilities.satisfiedByNetworkCapabilities(configCaps);
+ return request.canBeSatisfiedBy(builder.build());
}
private String getLogTag() {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 703bfab6d868..3cfa00eb6079 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -963,18 +963,18 @@ public class VcnGatewayConnection extends StateMachine {
@VisibleForTesting(visibility = Visibility.PRIVATE)
static NetworkCapabilities buildNetworkCapabilities(
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
- final NetworkCapabilities caps = new NetworkCapabilities();
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
- caps.addTransportType(TRANSPORT_CELLULAR);
- caps.addCapability(NET_CAPABILITY_NOT_CONGESTED);
- caps.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ builder.addTransportType(TRANSPORT_CELLULAR);
+ builder.addCapability(NET_CAPABILITY_NOT_CONGESTED);
+ builder.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
// Add exposed capabilities
for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) {
- caps.addCapability(cap);
+ builder.addCapability(cap);
}
- return caps;
+ return builder.build();
}
private static LinkProperties buildConnectedLinkProperties(
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index 7f5b23c9db6f..b9babae4c6b7 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -21,9 +21,9 @@ import android.content.Context;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.os.Looper;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
-import android.util.SparseArray;
import java.util.Objects;
import java.util.Set;
@@ -40,7 +40,13 @@ public class VcnNetworkProvider extends NetworkProvider {
private static final String TAG = VcnNetworkProvider.class.getSimpleName();
private final Set<NetworkRequestListener> mListeners = new ArraySet<>();
- private final SparseArray<NetworkRequestEntry> mRequests = new SparseArray<>();
+
+ /**
+ * Cache of NetworkRequest(s), scores and network providers, keyed by NetworkRequest
+ *
+ * <p>NetworkRequests are immutable once created, and therefore can be used as stable keys.
+ */
+ private final ArrayMap<NetworkRequest, NetworkRequestEntry> mRequests = new ArrayMap<>();
public VcnNetworkProvider(Context context, Looper looper) {
super(context, looper, VcnNetworkProvider.class.getSimpleName());
@@ -51,8 +57,8 @@ public class VcnNetworkProvider extends NetworkProvider {
mListeners.add(listener);
// Send listener all cached requests
- for (int i = 0; i < mRequests.size(); i++) {
- notifyListenerForEvent(listener, mRequests.valueAt(i));
+ for (NetworkRequestEntry entry : mRequests.values()) {
+ notifyListenerForEvent(listener, entry);
}
}
@@ -75,7 +81,9 @@ public class VcnNetworkProvider extends NetworkProvider {
request, score, providerId));
final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId);
- mRequests.put(request.requestId, entry);
+
+ // NetworkRequests are immutable once created, and therefore can be used as stable keys.
+ mRequests.put(request, entry);
// TODO(b/176939047): Intelligently route requests to prioritized VcnInstances (based on
// Default Data Sub, or similar)
@@ -86,7 +94,7 @@ public class VcnNetworkProvider extends NetworkProvider {
@Override
public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {
- mRequests.remove(request.requestId);
+ mRequests.remove(request);
}
private static class NetworkRequestEntry {
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index fe3b03abc79b..e0f5408a1537 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -138,6 +138,11 @@ public class Vibration {
return mStatus != Status.RUNNING;
}
+ /** Return true is effect is a repeating vibration. */
+ public boolean isRepeating() {
+ return mEffect.getDuration() == Long.MAX_VALUE;
+ }
+
/** Return the effect that should be played by this vibration. */
@Nullable
public CombinedVibrationEffect getEffect() {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 53552526c936..6f391f3bbc92 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -114,10 +114,6 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
}
}
- public Vibration getVibration() {
- return mVibration;
- }
-
@Override
public void binderDied() {
cancel();
@@ -156,6 +152,10 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
}
}
+ public Vibration getVibration() {
+ return mVibration;
+ }
+
@VisibleForTesting
SparseArray<VibratorController> getVibrators() {
return mVibrators;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b1da2f0fe888..4e359f2577c2 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2180,6 +2180,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/** @return Root task of this activity, null if there is no task. */
+ @Nullable
Task getRootTask() {
return task != null ? task.getRootTask() : null;
}
@@ -2188,6 +2189,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return task != null ? task.getRootTaskId() : INVALID_TASK_ID;
}
+ /** @return the first organized parent task. */
+ @Nullable
+ Task getOrganizedTask() {
+ return task != null ? task.getOrganizedTask() : null;
+ }
+
@Override
@Nullable
TaskDisplayArea getDisplayArea() {
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
deleted file mode 100644
index 3c8cf4edf733..000000000000
--- a/services/core/java/com/android/server/wm/BarController.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.annotation.NonNull;
-import android.graphics.Rect;
-
-/**
- * Controls state/behavior specific to a system bar window.
- */
-public class BarController {
- private final int mWindowType;
-
- private final Rect mContentFrame = new Rect();
-
- BarController(int windowType) {
- mWindowType = windowType;
- }
-
- /**
- * Sets the frame within which the bar will display its content.
- *
- * This is used to determine if letterboxes interfere with the display of such content.
- */
- void setContentFrame(Rect frame) {
- mContentFrame.set(frame);
- }
-
- private Rect getContentFrame(@NonNull WindowState win) {
- final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType);
- return rotatedContentFrame != null ? rotatedContentFrame : mContentFrame;
- }
-
- boolean isLightAppearanceAllowed(WindowState win) {
- if (win == null) {
- return true;
- }
- return !win.isLetterboxedOverlappingWith(getContentFrame(win));
- }
-
- /**
- * @return {@code true} if bar is allowed to be fully transparent when given window is show.
- *
- * <p>Prevents showing a transparent bar over a letterboxed activity which can make
- * notification icons or navigation buttons unreadable due to contrast between letterbox
- * background and an activity. For instance, this happens when letterbox background is solid
- * black while activity is white. To resolve this, only semi-transparent bars are allowed to
- * be drawn over letterboxed activity.
- */
- boolean isFullyTransparentAllowed(WindowState win) {
- if (win == null) {
- return true;
- }
- return win.isFullyTransparentBarAllowed(getContentFrame(win));
- }
-}
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index dbad8b364270..a725dd34fbf6 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -24,6 +24,9 @@ import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import android.app.ActivityManager;
import android.app.AppGlobals;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.res.CompatibilityInfo;
@@ -32,6 +35,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
@@ -63,6 +67,58 @@ public final class CompatModePackages {
// Compatibility state: compatibility mode is enabled.
private static final int COMPAT_FLAG_ENABLED = 1<<1;
+ /**
+ * CompatModePackages#DOWNSCALED is the gatekeeper of all per-app buffer downscaling
+ * changes. Disabling this change will prevent the following scaling factors from working:
+ * CompatModePackages#DOWNSCALE_87_5
+ * CompatModePackages#DOWNSCALE_75
+ * CompatModePackages#DOWNSCALE_62_5
+ * CompatModePackages#DOWNSCALE_50
+ *
+ * If CompatModePackages#DOWNSCALED is enabled for an app package, then the app will be forcibly
+ * resized to the highest enabled scaling factor e.g. 87.5% if both 87.5% and 75% were
+ * enabled.
+ */
+ @ChangeId
+ @Disabled
+ private static final long DOWNSCALED = 168419799L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_87_5 for a package will force the app to assume it's
+ * running on a display with 87.5% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ private static final long DOWNSCALE_87_5 = 176926753L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_75 for a package will force the app to assume it's
+ * running on a display with 75% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ private static final long DOWNSCALE_75 = 176926829L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_62_5 for a package will force the app to assume it's
+ * running on a display with 62.5% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ private static final long DOWNSCALE_62_5 = 176926771L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_50 for a package will force the app to assume it's
+ * running on a display with 50% vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ private static final long DOWNSCALE_50 = 176926741L;
+
private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
private static final int MSG_WRITE = 300;
@@ -191,11 +247,39 @@ public final class CompatModePackages {
mHandler.sendMessageDelayed(msg, 10000);
}
+ float getCompatScale(String packageName, int uid) {
+ if (!CompatChanges.isChangeEnabled(
+ DOWNSCALED, packageName, UserHandle.getUserHandleForUid(uid))) {
+ return 1f;
+ }
+ if (CompatChanges.isChangeEnabled(
+ DOWNSCALE_87_5, packageName, UserHandle.getUserHandleForUid(uid))) {
+ // 8/7 == (1 / 0.875) ~= 1.14285714286
+ return 8f / 7f;
+ }
+ if (CompatChanges.isChangeEnabled(
+ DOWNSCALE_75, packageName, UserHandle.getUserHandleForUid(uid))) {
+ // 4/3 == (1 / 0.75) ~= 1.333333333
+ return 4f / 3f;
+ }
+ if (CompatChanges.isChangeEnabled(
+ DOWNSCALE_62_5, packageName, UserHandle.getUserHandleForUid(uid))) {
+ // (1 / 0.625) == 1.6
+ return 1.6f;
+ }
+ if (CompatChanges.isChangeEnabled(
+ DOWNSCALE_50, packageName, UserHandle.getUserHandleForUid(uid))) {
+ return 2f;
+ }
+ return 1f;
+ }
+
public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
final Configuration globalConfig = mService.getGlobalConfiguration();
+ final float requestedScale = getCompatScale(ai.packageName, ai.uid);
CompatibilityInfo ci = new CompatibilityInfo(ai, globalConfig.screenLayout,
globalConfig.smallestScreenWidthDp,
- (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
+ (getPackageFlags(ai.packageName) & COMPAT_FLAG_ENABLED) != 0, requestedScale);
//Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
return ci;
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 15483cb90ce2..f075d850c20f 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -462,6 +462,23 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
@Override
+ void resolveOverrideConfiguration(Configuration newParentConfiguration) {
+ super.resolveOverrideConfiguration(newParentConfiguration);
+ final Configuration resolvedConfig = getResolvedOverrideConfiguration();
+ final Rect overrideBounds = resolvedConfig.windowConfiguration.getBounds();
+ final Rect overrideAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
+ final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+
+ // If there is no override of appBounds, restrict appBounds to the override bounds.
+ if (!overrideBounds.isEmpty() && (overrideAppBounds == null || overrideAppBounds.isEmpty())
+ && parentAppBounds != null && !parentAppBounds.isEmpty()) {
+ final Rect appBounds = new Rect(overrideBounds);
+ appBounds.intersect(parentAppBounds);
+ resolvedConfig.windowConfiguration.setAppBounds(appBounds);
+ }
+ }
+
+ @Override
boolean isOrganized() {
return mOrganizer != null;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ece101d2e605..d5d06f9a0033 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -670,8 +670,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Used in updating override configurations
private final Configuration mTempConfig = new Configuration();
- // Used in performing layout
- private boolean mTmpWindowsBehindIme;
+ // Used in performing layout, to record the insets provided by other windows above the current
+ // window.
+ private InsetsState mTmpAboveInsetsState = new InsetsState();
/**
* Used to prevent recursions when calling
@@ -770,17 +771,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
+ " parentHidden=" + w.isParentWindowHidden());
}
- // Sets mBehindIme for each window. Windows behind IME can get IME insets.
- if (w.mBehindIme != mTmpWindowsBehindIme) {
- w.mBehindIme = mTmpWindowsBehindIme;
- if (getInsetsStateController().getRawInsetsState().getSourceOrDefaultVisibility(
- ITYPE_IME)) {
- // If IME is invisible, behind IME or not doesn't make the insets different.
- mWinInsetsChanged.add(w);
- }
- }
- if (w == mInputMethodWindow) {
- mTmpWindowsBehindIme = true;
+ // Sets mAboveInsets for each window. Windows behind the window providing the insets can
+ // receive the insets.
+ if (!w.mAboveInsetsState.equals(mTmpAboveInsetsState)) {
+ w.mAboveInsetsState.set(mTmpAboveInsetsState);
+ mWinInsetsChanged.add(w);
}
// If this view is GONE, then skip it -- keep the current frame, and let the caller know
@@ -816,8 +811,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
+ " mContainingFrame=" + w.getContainingFrame()
+ " mDisplayFrame=" + w.getDisplayFrame());
}
+ provideInsetsByWindow(w);
};
+ private void provideInsetsByWindow(WindowState w) {
+ for (int i = 0; i < w.mProvidedInsetsSources.size(); i++) {
+ final InsetsSource providedSource = w.mProvidedInsetsSources.valueAt(i);
+ mTmpAboveInsetsState.addSource(providedSource);
+ }
+ }
+
private final Consumer<WindowState> mPerformLayoutAttached = w -> {
if (w.mLayoutAttached) {
if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame
@@ -4283,14 +4286,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
+ " dh=" + mDisplayInfo.logicalHeight);
}
+ // Used to indicate that we have processed the insets windows. This needs to be after
+ // beginLayoutLw to ensure the raw insets state display related info is initialized.
+ final InsetsState rawInsetsState = getInsetsStateController().getRawInsetsState();
+ mTmpAboveInsetsState = new InsetsState();
+ mTmpAboveInsetsState.setDisplayFrame(rawInsetsState.getDisplayFrame());
+ mTmpAboveInsetsState.setDisplayCutout(rawInsetsState.getDisplayCutout());
+ mTmpAboveInsetsState.mirrorAlwaysVisibleInsetsSources(rawInsetsState);
+
int seq = mLayoutSeq + 1;
if (seq < 0) seq = 0;
mLayoutSeq = seq;
mTmpInitial = initial;
- // Used to indicate that we have processed the IME window.
- mTmpWindowsBehindIme = false;
// First perform layout of any root windows (not attached to another window).
forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
@@ -5485,14 +5494,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Checks whether the given activity is in size compatibility mode and notifies the change. */
void handleActivitySizeCompatModeIfNeeded(ActivityRecord r) {
- if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
+ final Task organizedTask = r.getOrganizedTask();
+ if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
+ || organizedTask == null) {
// The callback is only interested in the foreground changes of fullscreen activity.
return;
}
if (!r.inSizeCompatMode()) {
if (mLastCompatModeActivity != null) {
+ // TODO(b/178327644) Remove notifySizeCompatModeActivityChanged
mAtmService.getTaskChangeNotificationController()
.notifySizeCompatModeActivityChanged(mDisplayId, null /* activityToken */);
+ // This will do nothing until SizeCompatModeActivityController is moved to shell
+ organizedTask.onSizeCompatActivityChanged();
}
mLastCompatModeActivity = null;
return;
@@ -5501,8 +5515,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return;
}
mLastCompatModeActivity = r;
+ // TODO(b/178327644) Remove notifySizeCompatModeActivityChanged
mAtmService.getTaskChangeNotificationController()
.notifySizeCompatModeActivityChanged(mDisplayId, r.appToken);
+ // This will do nothing until SizeCompatModeActivityController is moved to shell
+ organizedTask.onSizeCompatActivityChanged();
}
boolean isUidPresent(int uid) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f52cb09bc201..ed97848aece7 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -86,6 +86,7 @@ import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP;
import static android.view.WindowManagerPolicyConstants.ALT_BAR_UNKNOWN;
import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
@@ -305,8 +306,7 @@ public class DisplayPolicy {
private boolean mLastImmersiveMode;
- private final BarController mStatusBarController;
- private final BarController mNavigationBarController;
+ private final SparseArray<Rect> mBarContentFrames = new SparseArray<>();
// The windows we were told about in focusChanged.
private WindowState mFocusedWindow;
@@ -432,8 +432,8 @@ public class DisplayPolicy {
final int displayId = displayContent.getDisplayId();
- mStatusBarController = new BarController(TYPE_STATUS_BAR);
- mNavigationBarController = new BarController(TYPE_NAVIGATION_BAR);
+ mBarContentFrames.put(TYPE_STATUS_BAR, new Rect());
+ mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect());
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
@@ -1253,11 +1253,6 @@ public class DisplayPolicy {
displayFrames.mDisplayCutoutSafe.top);
}
- @VisibleForTesting
- BarController getStatusBarController() {
- return mStatusBarController;
- }
-
WindowState getStatusBar() {
return mStatusBar != null ? mStatusBar : mStatusBarAlt;
}
@@ -1473,7 +1468,7 @@ public class DisplayPolicy {
mSystemGestures.screenHeight = info.logicalHeight;
}
- private void layoutStatusBar(DisplayFrames displayFrames, Rect simulatedContentFrame) {
+ private void layoutStatusBar(DisplayFrames displayFrames, Rect contentFrame) {
// decide where the status bar goes ahead of time
if (mStatusBar == null) {
return;
@@ -1500,21 +1495,16 @@ public class DisplayPolicy {
statusBarBottom);
}
- // Tell the bar controller where the collapsed status bar content is.
sTmpRect.set(windowFrames.mFrame);
sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
sTmpRect.top = windowFrames.mFrame.top; // Ignore top display cutout inset
sTmpRect.bottom = statusBarBottom; // Use collapsed status bar size
- if (simulatedContentFrame != null) {
- simulatedContentFrame.set(sTmpRect);
- } else {
- mStatusBarController.setContentFrame(sTmpRect);
- }
+ contentFrame.set(sTmpRect);
}
- private void layoutNavigationBar(DisplayFrames displayFrames, Rect simulatedContentFrame) {
+ private int layoutNavigationBar(DisplayFrames displayFrames, Rect contentFrame) {
if (mNavigationBar == null) {
- return;
+ return NAV_BAR_INVALID;
}
final int uiMode = mDisplayContent.getConfiguration().uiMode;
@@ -1552,17 +1542,12 @@ public class DisplayPolicy {
windowFrames.setFrames(navigationFrame /* parentFrame */,
navigationFrame /* displayFrame */);
mNavigationBar.computeFrameAndUpdateSourceFrame();
- final Rect contentFrame = sTmpRect;
- contentFrame.set(windowFrames.mFrame);
- contentFrame.intersect(displayFrames.mDisplayCutoutSafe);
- if (simulatedContentFrame != null) {
- simulatedContentFrame.set(contentFrame);
- } else {
- mNavigationBarPosition = navBarPosition;
- mNavigationBarController.setContentFrame(contentFrame);
- }
+ sTmpRect.set(windowFrames.mFrame);
+ sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+ contentFrame.set(sTmpRect);
if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
+ return navBarPosition;
}
private boolean canReceiveInput(WindowState win) {
@@ -1609,11 +1594,12 @@ public class DisplayPolicy {
*/
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
if (win == mNavigationBar) {
- layoutNavigationBar(displayFrames, null /* simulatedContentFrame */);
+ mNavigationBarPosition = layoutNavigationBar(displayFrames,
+ mBarContentFrames.get(TYPE_NAVIGATION_BAR));
return;
}
if ((win == mStatusBar && !canReceiveInput(win))) {
- layoutStatusBar(displayFrames, null /* simulatedContentFrame */);
+ layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
return;
}
final WindowManager.LayoutParams attrs = win.getAttrs();
@@ -2622,7 +2608,7 @@ public class DisplayPolicy {
// Otherwise if it's dimming, clear the light flag.
appearance &= ~APPEARANCE_LIGHT_STATUS_BARS;
}
- if (!mStatusBarController.isLightAppearanceAllowed(statusColorWin)) {
+ if (!isLightBarAllowed(statusColorWin, TYPE_STATUS_BAR)) {
appearance &= ~APPEARANCE_LIGHT_STATUS_BARS;
}
}
@@ -2685,7 +2671,7 @@ public class DisplayPolicy {
// Clear the light flag for dimming window.
appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
}
- if (!mNavigationBarController.isLightAppearanceAllowed(navColorWin)) {
+ if (!isLightBarAllowed(navColorWin, TYPE_NAVIGATION_BAR)) {
appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
}
}
@@ -2739,7 +2725,36 @@ public class DisplayPolicy {
return appearance;
}
- private boolean drawsBarBackground(WindowState win, BarController controller) {
+ private boolean isLightBarAllowed(WindowState win, int windowType) {
+ if (win == null) {
+ return true;
+ }
+ return !win.isLetterboxedOverlappingWith(getBarContentFrameForWindow(win, windowType));
+ }
+
+ private Rect getBarContentFrameForWindow(WindowState win, int windowType) {
+ final Rect rotatedBarFrame = win.mToken.getFixedRotationBarContentFrame(windowType);
+ return rotatedBarFrame != null ? rotatedBarFrame : mBarContentFrames.get(windowType);
+ }
+
+ /**
+ * @return {@code true} if bar is allowed to be fully transparent when given window is show.
+ *
+ * <p>Prevents showing a transparent bar over a letterboxed activity which can make
+ * notification icons or navigation buttons unreadable due to contrast between letterbox
+ * background and an activity. For instance, this happens when letterbox background is solid
+ * black while activity is white. To resolve this, only semi-transparent bars are allowed to
+ * be drawn over letterboxed activity.
+ */
+ @VisibleForTesting
+ boolean isFullyTransparentAllowed(WindowState win, int windowType) {
+ if (win == null) {
+ return true;
+ }
+ return win.isFullyTransparentBarAllowed(getBarContentFrameForWindow(win, windowType));
+ }
+
+ private boolean drawsBarBackground(WindowState win) {
if (win == null) {
return true;
}
@@ -2752,27 +2767,19 @@ public class DisplayPolicy {
return forceDrawsSystemBars || drawsSystemBars;
}
- private boolean drawsStatusBarBackground(WindowState win) {
- return drawsBarBackground(win, mStatusBarController);
- }
-
- private boolean drawsNavigationBarBackground(WindowState win) {
- return drawsBarBackground(win, mNavigationBarController);
- }
-
/** @return the current visibility flags with the status bar opacity related flags toggled. */
private int configureStatusBarOpacity(int appearance) {
final boolean fullscreenDrawsBackground =
- drawsStatusBarBackground(mTopFullscreenOpaqueWindowState);
+ drawsBarBackground(mTopFullscreenOpaqueWindowState);
final boolean dockedDrawsBackground =
- drawsStatusBarBackground(mTopDockedOpaqueWindowState);
+ drawsBarBackground(mTopDockedOpaqueWindowState);
if (fullscreenDrawsBackground && dockedDrawsBackground) {
appearance &= ~APPEARANCE_OPAQUE_STATUS_BARS;
}
- if (!mStatusBarController.isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState)
- || !mStatusBarController.isFullyTransparentAllowed(mTopDockedOpaqueWindowState)) {
+ if (!isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState, TYPE_STATUS_BAR)
+ || !isFullyTransparentAllowed(mTopDockedOpaqueWindowState, TYPE_STATUS_BAR)) {
appearance |= APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
}
@@ -2788,9 +2795,9 @@ public class DisplayPolicy {
final boolean freeformRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
final boolean fullscreenDrawsBackground =
- drawsNavigationBarBackground(mTopFullscreenOpaqueWindowState);
+ drawsBarBackground(mTopFullscreenOpaqueWindowState);
final boolean dockedDrawsBackground =
- drawsNavigationBarBackground(mTopDockedOpaqueWindowState);
+ drawsBarBackground(mTopDockedOpaqueWindowState);
if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) {
if (fullscreenDrawsBackground && dockedDrawsBackground) {
@@ -2818,9 +2825,8 @@ public class DisplayPolicy {
}
}
- if (!mNavigationBarController.isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState)
- || !mNavigationBarController.isFullyTransparentAllowed(
- mTopDockedOpaqueWindowState)) {
+ if (!isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState, TYPE_NAVIGATION_BAR)
+ || !isFullyTransparentAllowed(mTopDockedOpaqueWindowState, TYPE_NAVIGATION_BAR)) {
appearance |= APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 1692df6d82cd..c6c7fe083b16 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -151,6 +151,7 @@ class InsetsSourceProvider {
// animate-out as new one animates-in.
mWin.cancelAnimation();
mWin.mPendingPositionChanged = null;
+ mWin.mProvidedInsetsSources.remove(mSource.getType());
}
ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win);
mWin = win;
@@ -160,11 +161,14 @@ class InsetsSourceProvider {
setServerVisible(false);
mSource.setFrame(new Rect());
mSource.setVisibleFrame(null);
- } else if (mControllable) {
- mWin.setControllableInsetProvider(this);
- if (mPendingControlTarget != null) {
- updateControlForTarget(mPendingControlTarget, true /* force */);
- mPendingControlTarget = null;
+ } else {
+ mWin.mProvidedInsetsSources.put(mSource.getType(), mSource);
+ if (mControllable) {
+ mWin.setControllableInsetProvider(this);
+ if (mPendingControlTarget != null) {
+ updateControlForTarget(mPendingControlTarget, true /* force */);
+ mPendingControlTarget = null;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 267f67759a24..3ba7b7d96e47 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -104,6 +104,8 @@ class InsetsStateController {
* visible to the target. e.g., the source which represents the target window itself, and the
* IME source when the target is above IME. We also need to exclude certain types of insets
* source for client within specific windowing modes.
+ * This is to get the insets for a window layout on the screen. If the window is not there, use
+ * the {@link #getInsetsForWindowMetrics} to get insets instead.
*
* @param target The window associate with the perspective.
* @return The state stripped of the necessary information.
@@ -117,8 +119,8 @@ class InsetsStateController {
final @InternalInsetsType int type = provider != null
? provider.getSource().getType() : ITYPE_INVALID;
return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
- isAboveIme(target),
- target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() : mState);
+ target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() :
+ target.mAboveInsetsState);
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
@@ -133,19 +135,7 @@ class InsetsStateController {
final @WindowingMode int windowingMode = token != null
? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
- return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token), mState);
- }
-
- private boolean isAboveIme(WindowContainer target) {
- final WindowState imeWindow = mDisplayContent.mInputMethodWindow;
- if (target == null || imeWindow == null) {
- return false;
- }
- if (target instanceof WindowState) {
- final WindowState win = (WindowState) target;
- return win.needsRelativeLayeringToIme() || !win.mBehindIme;
- }
- return false;
+ return getInsetsForTarget(type, windowingMode, alwaysOnTop, mState);
}
private static @InternalInsetsType
@@ -181,10 +171,12 @@ class InsetsStateController {
* @see #getInsetsForWindowMetrics
*/
private InsetsState getInsetsForTarget(@InternalInsetsType int type,
- @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme,
- @NonNull InsetsState state) {
+ @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state) {
+ boolean stateCopied = false;
+
if (type != ITYPE_INVALID) {
state = new InsetsState(state);
+ stateCopied = true;
state.removeSource(type);
// Navigation bar doesn't get influenced by anything else
@@ -219,23 +211,15 @@ class InsetsStateController {
if (WindowConfiguration.isFloating(windowingMode)
|| (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
- state = new InsetsState(state);
+ if (!stateCopied) {
+ state = new InsetsState(state);
+ stateCopied = true;
+ }
state.removeSource(ITYPE_STATUS_BAR);
state.removeSource(ITYPE_NAVIGATION_BAR);
state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
}
- if (aboveIme) {
- InsetsSource imeSource = state.peekSource(ITYPE_IME);
- if (imeSource != null && imeSource.isVisible()) {
- imeSource = new InsetsSource(imeSource);
- imeSource.setVisible(false);
- imeSource.setFrame(0, 0, 0, 0);
- state = new InsetsState(state);
- state.addSource(imeSource);
- }
- }
-
return state;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6a3110f52c91..f3f608bb9070 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3082,6 +3082,20 @@ class Task extends WindowContainer<WindowContainer> {
return parentTask == null ? this : parentTask.getRootTask();
}
+ /** @return the first organized task. */
+ @Nullable
+ Task getOrganizedTask() {
+ if (isOrganized()) {
+ return this;
+ }
+ final WindowContainer parent = getParent();
+ if (parent == null) {
+ return null;
+ }
+ final Task parentTask = parent.asTask();
+ return parentTask == null ? null : parentTask.getOrganizedTask();
+ }
+
// TODO(task-merge): Figure out what's the right thing to do for places that used it.
boolean isRootTask() {
return getRootTask() == this;
@@ -4172,6 +4186,14 @@ class Task extends WindowContainer<WindowContainer> {
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
: null;
+ info.topActivityToken = mReuseActivitiesReport.top != null
+ ? mReuseActivitiesReport.top.appToken
+ : null;
+ // Whether the direct top activity is in size compat mode on foreground.
+ info.topActivityInSizeCompat = mReuseActivitiesReport.top != null
+ && mReuseActivitiesReport.top.getOrganizedTask() == this
+ && mReuseActivitiesReport.top.inSizeCompatMode()
+ && mReuseActivitiesReport.top.isState(RESUMED);
info.launchCookies.clear();
info.addLaunchCookie(mLaunchCookie);
forAllActivities(r -> {
@@ -5221,6 +5243,12 @@ class Task extends WindowContainer<WindowContainer> {
}
}
+ /** Called when the top activity in the Root Task enters or exits size compat mode. */
+ void onSizeCompatActivityChanged() {
+ // Trigger TaskInfoChanged to update the size compat restart button.
+ dispatchTaskInfoChangedIfNeeded(true /* force */);
+ }
+
/**
* See {@link WindowContainerTransaction#setBoundsChangeTransaction}. In short this
* transaction will be consumed by the next BASE_APPLICATION window within our hierarchy
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 159760491ff1..025037688c8c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -212,6 +212,7 @@ import android.os.Trace;
import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
@@ -440,6 +441,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
float mGlobalScale=1;
float mLastGlobalScale=1;
float mInvGlobalScale=1;
+ float mOverrideScale = 1;
float mHScale=1, mVScale=1;
float mLastHScale=1, mLastVScale=1;
final Matrix mTmpMatrix = new Matrix();
@@ -647,9 +649,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean mSeamlesslyRotated = false;
/**
- * Indicates if this window is behind IME. Only windows behind IME can get insets from IME.
+ * The insets state of sources provided by windows above the current window.
*/
- boolean mBehindIme = false;
+ InsetsState mAboveInsetsState = new InsetsState();
+
+ /**
+ * The insets sources provided by this window.
+ */
+ ArrayMap<Integer, InsetsSource> mProvidedInsetsSources = new ArrayMap<>();
/**
* Surface insets from the previous call to relayout(), used to track
@@ -1008,6 +1015,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
mLayer = 0;
+ mOverrideScale = mWmService.mAtmService.mCompatModePackages.getCompatScale(
+ mAttrs.packageName, s.mUid);
// Make sure we initial all fields before adding to parentWindow, to prevent exception
// during onDisplayChanged.
@@ -1040,8 +1049,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mSession.windowAddedLocked(mAttrs.packageName);
}
+ /**
+ * @return {@code true} if the application runs in size compatibility mode or has an app level
+ * scaling override set.
+ * @see CompatModePackages#getCompatScale
+ * @see android.content.res.CompatibilityInfo#supportsScreen
+ * @see ActivityRecord#inSizeCompatMode()
+ */
boolean inSizeCompatMode() {
- return inSizeCompatMode(mAttrs, mActivityRecord);
+ return mOverrideScale != 1f || inSizeCompatMode(mAttrs, mActivityRecord);
}
/**
@@ -1676,7 +1692,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void prelayout() {
if (inSizeCompatMode()) {
- mGlobalScale = mToken.getSizeCompatScale();
+ if (mOverrideScale != 1f) {
+ mGlobalScale = mToken.hasSizeCompatBounds()
+ ? mToken.getSizeCompatScale() * mOverrideScale
+ : mOverrideScale;
+ } else {
+ mGlobalScale = mToken.getSizeCompatScale();
+ }
mInvGlobalScale = 1 / mGlobalScale;
} else {
mGlobalScale = mInvGlobalScale = 1;
@@ -2634,8 +2656,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// scaling but the existing logic doesn't expect that. The result is that the already-
// scaled region ends up getting sent to surfaceflinger which then applies the scale
// (again). Until this is resolved, apply an inverse-scale here.
- if (mActivityRecord != null && mActivityRecord.hasSizeCompatBounds()
- && mGlobalScale != 1.f) {
+ if (mInvGlobalScale != 1.f) {
region.scale(mInvGlobalScale);
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index dc15b0749bb1..5b587e9859d8 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -26,14 +26,14 @@
// Log debug messages about InputDispatcherPolicy
#define DEBUG_INPUT_DISPATCHER_POLICY 0
-
-#include <atomic>
-#include <cinttypes>
-#include <limits.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
+#include <android/os/IInputConstants.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
+#include <limits.h>
+#include <atomic>
+#include <cinttypes>
#include <utils/Log.h>
#include <utils/Looper.h>
@@ -46,6 +46,7 @@
#include <input/SpriteController.h>
#include <ui/Region.h>
+#include <batteryservice/include/batteryservice/BatteryServiceConstants.h>
#include <inputflinger/InputManager.h>
#include <android_os_MessageQueue.h>
@@ -1908,6 +1909,20 @@ static jintArray nativeGetVibratorIds(JNIEnv* env, jclass clazz, jlong ptr, jint
return vibIdArray;
}
+static jint nativeGetBatteryCapacity(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ std::optional<int32_t> ret = im->getInputManager()->getReader()->getBatteryCapacity(deviceId);
+ return static_cast<jint>(ret.value_or(android::os::IInputConstants::INVALID_BATTERY_CAPACITY));
+}
+
+static jint nativeGetBatteryStatus(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ std::optional<int32_t> ret = im->getInputManager()->getReader()->getBatteryStatus(deviceId);
+ return static_cast<jint>(ret.value_or(BATTERY_STATUS_UNKNOWN));
+}
+
static void nativeReloadKeyboardLayouts(JNIEnv* /* env */,
jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -2163,6 +2178,8 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate},
{"nativeIsVibrating", "(JI)Z", (void*)nativeIsVibrating},
{"nativeGetVibratorIds", "(JI)[I", (void*)nativeGetVibratorIds},
+ {"nativeGetBatteryCapacity", "(JI)I", (void*)nativeGetBatteryCapacity},
+ {"nativeGetBatteryStatus", "(JI)I", (void*)nativeGetBatteryStatus},
{"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts},
{"nativeReloadDeviceAliases", "(J)V", (void*)nativeReloadDeviceAliases},
{"nativeDump", "(J)Ljava/lang/String;", (void*)nativeDump},
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5ced8680c095..da8d82450769 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -342,6 +342,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.contentsuggestions.ContentSuggestionsManagerService";
private static final String SEARCH_UI_MANAGER_SERVICE_CLASS =
"com.android.server.searchui.SearchUiManagerService";
+ private static final String SMARTSPACE_MANAGER_SERVICE_CLASS =
+ "com.android.server.smartspace.SmartspaceManagerService";
private static final String DEVICE_IDLE_CONTROLLER_CLASS =
"com.android.server.DeviceIdleController";
private static final String BLOB_STORE_MANAGER_SERVICE_CLASS =
@@ -1676,6 +1678,12 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(SEARCH_UI_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ // Smartspace manager service
+ // TODO: add deviceHasConfigString(context, R.string.config_defaultSmartspaceService)
+ t.traceBegin("StartSmartspaceService");
+ mSystemServiceManager.startService(SMARTSPACE_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+
t.traceBegin("InitConnectivityModuleConnector");
try {
ConnectivityModuleConnector.getInstance().init(context);
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 9a9a17112245..444f9c685a1b 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -264,7 +264,8 @@ public class DataManager {
return new ConversationChannel(shortcutInfo, uid, parentChannel,
parentChannelGroup,
conversationInfo.getLastEventTimestamp(),
- hasActiveNotifications(packageName, userId, shortcutId));
+ hasActiveNotifications(packageName, userId, shortcutId), false,
+ getStatuses(conversationInfo));
}
/** Returns the cached non-customized recent conversations. */
@@ -404,6 +405,10 @@ public class DataManager {
String conversationId) {
ConversationStore cs = getConversationStoreOrThrow(packageName, userId);
ConversationInfo conversationInfo = getConversationInfoOrThrow(cs, conversationId);
+ return getStatuses(conversationInfo);
+ }
+
+ private @NonNull List<ConversationStatus> getStatuses(ConversationInfo conversationInfo) {
Collection<ConversationStatus> statuses = conversationInfo.getStatuses();
if (statuses != null) {
final ArrayList<ConversationStatus> list = new ArrayList<>(statuses.size());
diff --git a/services/smartspace/Android.bp b/services/smartspace/Android.bp
new file mode 100644
index 000000000000..fcf780d4d927
--- /dev/null
+++ b/services/smartspace/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+ name: "services.smartspace-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.smartspace",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.smartspace-sources"],
+ libs: ["services.core"],
+}
diff --git a/services/smartspace/java/com/android/server/smartspace/RemoteSmartspaceService.java b/services/smartspace/java/com/android/server/smartspace/RemoteSmartspaceService.java
new file mode 100644
index 000000000000..3b5a5a51536e
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/RemoteSmartspaceService.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.smartspace;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.service.smartspace.ISmartspaceService;
+import android.text.format.DateUtils;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+
+
+/**
+ * Proxy to the {@link android.service.smartspace.SmartspaceService} implementation in another
+ * process.
+ */
+public class RemoteSmartspaceService extends
+ AbstractMultiplePendingRequestsRemoteService<RemoteSmartspaceService,
+ ISmartspaceService> {
+
+ private static final String TAG = "RemoteSmartspaceService";
+
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+
+ private final RemoteSmartspaceServiceCallbacks mCallback;
+
+ public RemoteSmartspaceService(Context context, String serviceInterface,
+ ComponentName componentName, int userId,
+ RemoteSmartspaceServiceCallbacks callback, boolean bindInstantServiceAllowed,
+ boolean verbose) {
+ super(context, serviceInterface, componentName, userId, callback,
+ context.getMainThreadHandler(),
+ bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
+ verbose, /* initialCapacity= */ 1);
+ mCallback = callback;
+ }
+
+ @Override
+ protected ISmartspaceService getServiceInterface(IBinder service) {
+ return ISmartspaceService.Stub.asInterface(service);
+ }
+
+ @Override
+ protected long getTimeoutIdleBindMillis() {
+ return PERMANENT_BOUND_TIMEOUT_MS;
+ }
+
+ @Override
+ protected long getRemoteRequestMillis() {
+ return TIMEOUT_REMOTE_REQUEST_MILLIS;
+ }
+
+ /**
+ * Schedules a request to bind to the remote service.
+ */
+ public void reconnect() {
+ super.scheduleBind();
+ }
+
+ /**
+ * Schedule async request on remote service.
+ */
+ public void scheduleOnResolvedService(@NonNull AsyncRequest<ISmartspaceService> request) {
+ scheduleAsyncRequest(request);
+ }
+
+ /**
+ * Execute async request on remote service immediately instead of sending it to Handler queue.
+ */
+ public void executeOnResolvedService(@NonNull AsyncRequest<ISmartspaceService> request) {
+ executeAsyncRequest(request);
+ }
+
+ /**
+ * Failure callback
+ */
+ public interface RemoteSmartspaceServiceCallbacks
+ extends VultureCallback<RemoteSmartspaceService> {
+
+ /**
+ * Notifies a the failure or timeout of a remote call.
+ */
+ void onFailureOrTimeout(boolean timedOut);
+
+ /**
+ * Notifies change in connected state of the remote service.
+ */
+ void onConnectedStateChanged(boolean connected);
+ }
+
+ @Override // from AbstractRemoteService
+ protected void handleOnConnectedStateChanged(boolean connected) {
+ if (mCallback != null) {
+ mCallback.onConnectedStateChanged(connected);
+ }
+ }
+}
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
new file mode 100644
index 000000000000..169b85eb7e06
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.smartspace;
+
+import static android.Manifest.permission.MANAGE_SMARTSPACE;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.content.Context.SMARTSPACE_SERVICE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.app.smartspace.ISmartspaceCallback;
+import android.app.smartspace.ISmartspaceManager;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.io.FileDescriptor;
+import java.util.function.Consumer;
+
+/**
+ * A service used to return smartspace targets given a query.
+ */
+public class SmartspaceManagerService extends
+ AbstractMasterSystemService<SmartspaceManagerService, SmartspacePerUserService> {
+
+ private static final String TAG = SmartspaceManagerService.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
+ private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+
+ public SmartspaceManagerService(Context context) {
+ super(context, new FrameworkResourcesServiceNameResolver(context,
+ com.android.internal.R.string.config_defaultSmartspaceService), null,
+ PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
+ mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ }
+
+ @Override
+ protected SmartspacePerUserService newServiceLocked(int resolvedUserId, boolean disabled) {
+ return new SmartspacePerUserService(this, mLock, resolvedUserId);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(SMARTSPACE_SERVICE, new SmartspaceManagerStub());
+ }
+
+ @Override
+ protected void enforceCallingPermissionForManagement() {
+ getContext().enforceCallingPermission(MANAGE_SMARTSPACE, TAG);
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
+ final SmartspacePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.onPackageUpdatedLocked();
+ }
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
+ final SmartspacePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.onPackageRestartedLocked();
+ }
+ }
+
+ @Override
+ protected int getMaximumTemporaryServiceDurationMs() {
+ return MAX_TEMP_SERVICE_DURATION_MS;
+ }
+
+ private class SmartspaceManagerStub extends ISmartspaceManager.Stub {
+
+ @Override
+ public void createSmartspaceSession(@NonNull SmartspaceConfig smartspaceConfig,
+ @NonNull SmartspaceSessionId sessionId, @NonNull IBinder token) {
+ runForUserLocked("createSmartspaceSession", sessionId, (service) ->
+ service.onCreateSmartspaceSessionLocked(smartspaceConfig, sessionId, token));
+ }
+
+ @Override
+ public void notifySmartspaceEvent(SmartspaceSessionId sessionId,
+ SmartspaceTargetEvent event) {
+ runForUserLocked("notifySmartspaceEvent", sessionId,
+ (service) -> service.notifySmartspaceEventLocked(sessionId, event));
+ }
+
+ @Override
+ public void requestSmartspaceUpdate(SmartspaceSessionId sessionId) {
+ runForUserLocked("requestSmartspaceUpdate", sessionId,
+ (service) -> service.requestSmartspaceUpdateLocked(sessionId));
+ }
+
+ @Override
+ public void registerSmartspaceUpdates(@NonNull SmartspaceSessionId sessionId,
+ @NonNull ISmartspaceCallback callback) {
+ runForUserLocked("registerSmartspaceUpdates", sessionId,
+ (service) -> service.registerSmartspaceUpdatesLocked(sessionId, callback));
+ }
+
+ @Override
+ public void unregisterSmartspaceUpdates(SmartspaceSessionId sessionId,
+ ISmartspaceCallback callback) {
+ runForUserLocked("unregisterSmartspaceUpdates", sessionId,
+ (service) -> service.unregisterSmartspaceUpdatesLocked(sessionId, callback));
+ }
+
+ @Override
+ public void destroySmartspaceSession(@NonNull SmartspaceSessionId sessionId) {
+ runForUserLocked("destroySmartspaceSession", sessionId,
+ (service) -> service.onDestroyLocked(sessionId));
+ }
+
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) {
+ new SmartspaceManagerServiceShellCommand(SmartspaceManagerService.this)
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ private void runForUserLocked(@NonNull final String func,
+ @NonNull final SmartspaceSessionId sessionId,
+ @NonNull final Consumer<SmartspacePerUserService> c) {
+ ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
+ final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ sessionId.getUserId(), false, ALLOW_NON_FULL, null, null);
+
+ if (DEBUG) {
+ Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ }
+ if (!(mServiceNameResolver.isTemporary(userId)
+ || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
+
+ String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid();
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ final SmartspacePerUserService service = getServiceForUserLocked(userId);
+ c.accept(service);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+}
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerServiceShellCommand.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerServiceShellCommand.java
new file mode 100644
index 000000000000..4143418e112e
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerServiceShellCommand.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.smartspace;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * The shell command implementation for the SmartspaceManagerService.
+ */
+public class SmartspaceManagerServiceShellCommand extends ShellCommand {
+
+ private static final String TAG =
+ SmartspaceManagerServiceShellCommand.class.getSimpleName();
+
+ private final SmartspaceManagerService mService;
+
+ public SmartspaceManagerServiceShellCommand(@NonNull SmartspaceManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "set": {
+ final String what = getNextArgRequired();
+ switch (what) {
+ case "temporary-service": {
+ final int userId = Integer.parseInt(getNextArgRequired());
+ String serviceName = getNextArg();
+ if (serviceName == null) {
+ mService.resetTemporaryService(userId);
+ pw.println("SmartspaceService temporarily reset. ");
+ return 0;
+ }
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setTemporaryService(userId, serviceName, duration);
+ pw.println("SmartspaceService temporarily set to " + serviceName
+ + " for " + duration + "ms");
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ try (PrintWriter pw = getOutPrintWriter()) {
+ pw.println("SmartspaceManagerService commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println("");
+ pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the service implemtation.");
+ pw.println(" To reset, call with just the USER_ID argument.");
+ pw.println("");
+ }
+ }
+}
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java b/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java
new file mode 100644
index 000000000000..db4346830efb
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.app.smartspace.ISmartspaceCallback;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.service.smartspace.ISmartspaceService;
+import android.service.smartspace.SmartspaceService;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AbstractRemoteService;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+/**
+ * Per-user instance of {@link SmartspaceManagerService}.
+ */
+public class SmartspacePerUserService extends
+ AbstractPerUserSystemService<SmartspacePerUserService, SmartspaceManagerService>
+ implements RemoteSmartspaceService.RemoteSmartspaceServiceCallbacks {
+
+ private static final String TAG = SmartspacePerUserService.class.getSimpleName();
+ @GuardedBy("mLock")
+ private final ArrayMap<SmartspaceSessionId, SmartspaceSessionInfo> mSessionInfos =
+ new ArrayMap<>();
+ @Nullable
+ @GuardedBy("mLock")
+ private RemoteSmartspaceService mRemoteService;
+ /**
+ * When {@code true}, remote service died but service state is kept so it's restored after
+ * the system re-binds to it.
+ */
+ @GuardedBy("mLock")
+ private boolean mZombie;
+
+ protected SmartspacePerUserService(SmartspaceManagerService master,
+ Object lock, int userId) {
+ super(master, lock, userId);
+ }
+
+ @Override // from PerUserSystemService
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+ throws NameNotFoundException {
+
+ ServiceInfo si;
+ try {
+ si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+ PackageManager.GET_META_DATA, mUserId);
+ } catch (RemoteException e) {
+ throw new NameNotFoundException("Could not get service for " + serviceComponent);
+ }
+ // TODO(b/177858728): must check that either the service is from a system component,
+ // or it matches a service set by shell cmd (so it can be used on CTS tests and when
+ // OEMs are implementing the real service and also verify the proper permissions
+ return si;
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected boolean updateLocked(boolean disabled) {
+ final boolean enabledChanged = super.updateLocked(disabled);
+ if (enabledChanged) {
+ if (!isEnabledLocked()) {
+ // Clear the remote service for the next call
+ updateRemoteServiceLocked();
+ }
+ }
+ return enabledChanged;
+ }
+
+ /**
+ * Notifies the service of a new smartspace session.
+ */
+ @GuardedBy("mLock")
+ public void onCreateSmartspaceSessionLocked(@NonNull SmartspaceConfig smartspaceConfig,
+ @NonNull SmartspaceSessionId sessionId, @NonNull IBinder token) {
+ final boolean serviceExists = resolveService(sessionId,
+ s -> s.onCreateSmartspaceSession(smartspaceConfig, sessionId));
+
+ if (serviceExists && !mSessionInfos.containsKey(sessionId)) {
+ final SmartspaceSessionInfo sessionInfo = new SmartspaceSessionInfo(
+ sessionId, smartspaceConfig, token, () -> {
+ synchronized (mLock) {
+ onDestroyLocked(sessionId);
+ }
+ });
+ if (sessionInfo.linkToDeath()) {
+ mSessionInfos.put(sessionId, sessionInfo);
+ } else {
+ // destroy the session if calling process is already dead
+ onDestroyLocked(sessionId);
+ }
+ }
+ }
+
+ /**
+ * Records an smartspace event to the service.
+ */
+ @GuardedBy("mLock")
+ public void notifySmartspaceEventLocked(@NonNull SmartspaceSessionId sessionId,
+ @NonNull SmartspaceTargetEvent event) {
+ final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (sessionInfo == null) return;
+ resolveService(sessionId, s -> s.notifySmartspaceEvent(sessionId, event));
+ }
+
+ /**
+ * Requests the service to return smartspace results of an input query.
+ */
+ @GuardedBy("mLock")
+ public void requestSmartspaceUpdateLocked(@NonNull SmartspaceSessionId sessionId) {
+ final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (sessionInfo == null) return;
+ resolveService(sessionId,
+ s -> s.requestSmartspaceUpdate(sessionId));
+ }
+
+ /**
+ * Registers a callback for continuous updates of predicted apps or shortcuts.
+ */
+ @GuardedBy("mLock")
+ public void registerSmartspaceUpdatesLocked(@NonNull SmartspaceSessionId sessionId,
+ @NonNull ISmartspaceCallback callback) {
+ final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (sessionInfo == null) return;
+ final boolean serviceExists = resolveService(sessionId,
+ s -> s.registerSmartspaceUpdates(sessionId, callback));
+ if (serviceExists) {
+ sessionInfo.addCallbackLocked(callback);
+ }
+ }
+
+ /**
+ * Unregisters a callback for continuous updates of predicted apps or shortcuts.
+ */
+ @GuardedBy("mLock")
+ public void unregisterSmartspaceUpdatesLocked(@NonNull SmartspaceSessionId sessionId,
+ @NonNull ISmartspaceCallback callback) {
+ final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (sessionInfo == null) return;
+ final boolean serviceExists = resolveService(sessionId,
+ s -> s.unregisterSmartspaceUpdates(sessionId, callback));
+ if (serviceExists) {
+ sessionInfo.removeCallbackLocked(callback);
+ }
+ }
+
+ /**
+ * Notifies the service of the end of an existing smartspace session.
+ */
+ @GuardedBy("mLock")
+ public void onDestroyLocked(@NonNull SmartspaceSessionId sessionId) {
+ if (isDebug()) {
+ Slog.d(TAG, "onDestroyLocked(): sessionId=" + sessionId);
+ }
+ final SmartspaceSessionInfo sessionInfo = mSessionInfos.remove(sessionId);
+ if (sessionInfo == null) return;
+ resolveService(sessionId, s -> s.onDestroySmartspaceSession(sessionId));
+ sessionInfo.destroy();
+ }
+
+ @Override
+ public void onFailureOrTimeout(boolean timedOut) {
+ if (isDebug()) {
+ Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut);
+ }
+ // Do nothing, we are just proxying to the smartspace ui service
+ }
+
+ @Override
+ public void onConnectedStateChanged(boolean connected) {
+ if (isDebug()) {
+ Slog.d(TAG, "onConnectedStateChanged(): connected=" + connected);
+ }
+ if (connected) {
+ synchronized (mLock) {
+ if (mZombie) {
+ // Validation check - shouldn't happen
+ if (mRemoteService == null) {
+ Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
+ return;
+ }
+ mZombie = false;
+ resurrectSessionsLocked();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDied(RemoteSmartspaceService service) {
+ if (isDebug()) {
+ Slog.w(TAG, "onServiceDied(): service=" + service);
+ }
+ synchronized (mLock) {
+ mZombie = true;
+ }
+ updateRemoteServiceLocked();
+ }
+
+ @GuardedBy("mLock")
+ private void updateRemoteServiceLocked() {
+ if (mRemoteService != null) {
+ mRemoteService.destroy();
+ mRemoteService = null;
+ }
+ }
+
+ void onPackageUpdatedLocked() {
+ if (isDebug()) {
+ Slog.v(TAG, "onPackageUpdatedLocked()");
+ }
+ destroyAndRebindRemoteService();
+ }
+
+ void onPackageRestartedLocked() {
+ if (isDebug()) {
+ Slog.v(TAG, "onPackageRestartedLocked()");
+ }
+ destroyAndRebindRemoteService();
+ }
+
+ private void destroyAndRebindRemoteService() {
+ if (mRemoteService == null) {
+ return;
+ }
+
+ if (isDebug()) {
+ Slog.d(TAG, "Destroying the old remote service.");
+ }
+ mRemoteService.destroy();
+ mRemoteService = null;
+
+ synchronized (mLock) {
+ mZombie = true;
+ }
+ mRemoteService = getRemoteServiceLocked();
+ if (mRemoteService != null) {
+ if (isDebug()) {
+ Slog.d(TAG, "Rebinding to the new remote service.");
+ }
+ mRemoteService.reconnect();
+ }
+ }
+
+ /**
+ * Called after the remote service connected, it's used to restore state from a 'zombie'
+ * service (i.e., after it died).
+ */
+ private void resurrectSessionsLocked() {
+ final int numSessions = mSessionInfos.size();
+ if (isDebug()) {
+ Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on "
+ + numSessions + " sessions.");
+ }
+
+ for (SmartspaceSessionInfo sessionInfo : mSessionInfos.values()) {
+ sessionInfo.resurrectSessionLocked(this, sessionInfo.mToken);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ protected boolean resolveService(
+ @NonNull final SmartspaceSessionId sessionId,
+ @NonNull final AbstractRemoteService.AsyncRequest<ISmartspaceService> cb) {
+
+ final RemoteSmartspaceService service = getRemoteServiceLocked();
+ if (service != null) {
+ service.executeOnResolvedService(cb);
+ }
+ return service != null;
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteSmartspaceService getRemoteServiceLocked() {
+ if (mRemoteService == null) {
+ final String serviceName = getComponentNameLocked();
+ if (serviceName == null) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "getRemoteServiceLocked(): not set");
+ }
+ return null;
+ }
+ ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+ mRemoteService = new RemoteSmartspaceService(getContext(),
+ SmartspaceService.SERVICE_INTERFACE, serviceComponent, mUserId, this,
+ mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+ }
+
+ return mRemoteService;
+ }
+
+ private static final class SmartspaceSessionInfo {
+ private static final boolean DEBUG = false; // Do not submit with true
+ @NonNull
+ final IBinder mToken;
+ @NonNull
+ final IBinder.DeathRecipient mDeathRecipient;
+ @NonNull
+ private final SmartspaceSessionId mSessionId;
+ @NonNull
+ private final SmartspaceConfig mSmartspaceConfig;
+ private final RemoteCallbackList<ISmartspaceCallback> mCallbacks =
+ new RemoteCallbackList<ISmartspaceCallback>() {
+ @Override
+ public void onCallbackDied(ISmartspaceCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "Binder died for session Id=" + mSessionId
+ + " and callback=" + callback.asBinder());
+ }
+ if (mCallbacks.getRegisteredCallbackCount() == 0) {
+ destroy();
+ }
+ }
+ };
+
+ SmartspaceSessionInfo(
+ @NonNull final SmartspaceSessionId id,
+ @NonNull final SmartspaceConfig context,
+ @NonNull final IBinder token,
+ @NonNull final IBinder.DeathRecipient deathRecipient) {
+ if (DEBUG) {
+ Slog.d(TAG, "Creating SmartspaceSessionInfo for session Id=" + id);
+ }
+ mSessionId = id;
+ mSmartspaceConfig = context;
+ mToken = token;
+ mDeathRecipient = deathRecipient;
+ }
+
+ void addCallbackLocked(ISmartspaceCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "Storing callback for session Id=" + mSessionId
+ + " and callback=" + callback.asBinder());
+ }
+ mCallbacks.register(callback);
+ }
+
+ void removeCallbackLocked(ISmartspaceCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "Removing callback for session Id=" + mSessionId
+ + " and callback=" + callback.asBinder());
+ }
+ mCallbacks.unregister(callback);
+ }
+
+ boolean linkToDeath() {
+ try {
+ mToken.linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "Caller is dead before session can be started, sessionId: "
+ + mSessionId);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ void destroy() {
+ if (DEBUG) {
+ Slog.d(TAG, "Removing all callbacks for session Id=" + mSessionId
+ + " and " + mCallbacks.getRegisteredCallbackCount() + " callbacks.");
+ }
+ if (mToken != null) {
+ mToken.unlinkToDeath(mDeathRecipient, 0);
+ }
+ mCallbacks.kill();
+ }
+
+ void resurrectSessionLocked(SmartspacePerUserService service, IBinder token) {
+ int callbackCount = mCallbacks.getRegisteredCallbackCount();
+ if (DEBUG) {
+ Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
+ + ") for session Id=" + mSessionId + " and "
+ + callbackCount + " callbacks.");
+ }
+ service.onCreateSmartspaceSessionLocked(mSmartspaceConfig, mSessionId, token);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
index 0a35db56f35c..f7b24920f903 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
@@ -16,34 +16,61 @@
package com.android.server;
-import static com.android.server.testutils.TestUtils.assertExpectException;
-
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManagerInternal;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManager;
import android.hardware.vibrator.IVibrator;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
import android.os.CombinedVibrationEffect;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.IVibratorStateListener;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorInfo;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.view.InputDevice;
import androidx.test.InstrumentationRegistry;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.server.vibrator.FakeVibrator;
import com.android.server.vibrator.FakeVibratorControllerProvider;
import com.android.server.vibrator.VibratorController;
@@ -51,12 +78,16 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.Predicate;
/**
* Tests for {@link VibratorManagerService}.
@@ -67,39 +98,86 @@ import java.util.Map;
@Presubmit
public class VibratorManagerServiceTest {
+ private static final int TEST_TIMEOUT_MILLIS = 1_000;
private static final int UID = Process.ROOT_UID;
private static final String PACKAGE_NAME = "package";
+ private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
+ private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
+ .setBatterySaverEnabled(true).build();
private static final VibrationAttributes ALARM_ATTRS =
new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
+ private static final VibrationAttributes HAPTIC_FEEDBACK_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_TOUCH).build();
+ private static final VibrationAttributes NOTIFICATION_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_NOTIFICATION).build();
+ private static final VibrationAttributes RINGTONE_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_RINGTONE).build();
@Rule public MockitoRule rule = MockitoJUnit.rule();
+ @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Mock private VibratorManagerService.NativeWrapper mNativeWrapperMock;
+ @Mock private PackageManagerInternal mPackageManagerInternalMock;
@Mock private PowerManagerInternal mPowerManagerInternalMock;
@Mock private PowerSaveState mPowerSaveStateMock;
+ @Mock private AppOpsManager mAppOpsManagerMock;
+ @Mock private IInputManager mIInputManagerMock;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
+ private Context mContextSpy;
private TestLooper mTestLooper;
+ private FakeVibrator mVibrator;
+ private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
@Before
public void setUp() throws Exception {
mTestLooper = new TestLooper();
-
+ mVibrator = new FakeVibrator();
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+ InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
+
+ ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
+ when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+ when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibrator);
+ when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager);
+ when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock);
+ when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
+ when(mPackageManagerInternalMock.getSystemUiServiceComponent())
+ .thenReturn(new ComponentName("", ""));
when(mPowerManagerInternalMock.getLowPowerState(PowerManager.ServiceType.VIBRATION))
.thenReturn(mPowerSaveStateMock);
-
+ doAnswer(invocation -> {
+ mRegisteredPowerModeListener = invocation.getArgument(0);
+ return null;
+ }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
+
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_MEDIUM);
+
+ addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock);
addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
+
+ mTestLooper.startAutoDispatch();
}
@After
public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
}
private VibratorManagerService createService() {
VibratorManagerService service = new VibratorManagerService(
- InstrumentationRegistry.getContext(),
+ mContextSpy,
new VibratorManagerService.Injector() {
@Override
VibratorManagerService.NativeWrapper getNativeWrapper() {
@@ -172,6 +250,74 @@ public class VibratorManagerServiceTest {
}
@Test
+ public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createService();
+ IVibratorStateListener listenerMock = mockVibratorStateListener();
+ service.registerVibratorStateListener(1, listenerMock);
+
+ vibrate(service, VibrationEffect.createOneShot(40, 100), ALARM_ATTRS);
+ // Wait until service knows vibrator is on.
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+ // Wait until effect ends.
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ InOrder inOrderVerifier = inOrder(listenerMock);
+ // First notification done when listener is registered.
+ inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
+ inOrderVerifier.verify(listenerMock).onVibrating(eq(true));
+ inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
+ inOrderVerifier.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createService();
+ IVibratorStateListener listenerMock = mockVibratorStateListener();
+ service.registerVibratorStateListener(1, listenerMock);
+
+ vibrate(service, VibrationEffect.createOneShot(40, 100), ALARM_ATTRS);
+
+ // Wait until service knows vibrator is on.
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ service.unregisterVibratorStateListener(1, listenerMock);
+
+ // Wait until vibrator is off.
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ InOrder inOrderVerifier = inOrder(listenerMock);
+ // First notification done when listener is registered.
+ inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
+ inOrderVerifier.verify(listenerMock).onVibrating(eq(true));
+ inOrderVerifier.verify(listenerMock, atLeastOnce()).asBinder(); // unregister
+ inOrderVerifier.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void registerVibratorStateListener_multipleVibratorsAreTriggered() throws Exception {
+ mockVibrators(0, 1, 2);
+ VibratorManagerService service = createService();
+ IVibratorStateListener[] listeners = new IVibratorStateListener[3];
+ for (int i = 0; i < 3; i++) {
+ listeners[i] = mockVibratorStateListener();
+ service.registerVibratorStateListener(i, listeners[i]);
+ }
+
+ vibrate(service, CombinedVibrationEffect.startSynced()
+ .addVibrator(0, VibrationEffect.createOneShot(40, 100))
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .combine(), ALARM_ATTRS);
+ // Wait until service knows vibrator is on.
+ assertTrue(waitUntil(s -> s.isVibrating(0), service, TEST_TIMEOUT_MILLIS));
+
+ verify(listeners[0]).onVibrating(eq(true));
+ verify(listeners[1]).onVibrating(eq(true));
+ verify(listeners[2], never()).onVibrating(eq(true));
+ }
+
+ @Test
public void setAlwaysOnEffect_withMono_enablesAlwaysOnEffectToAllVibratorsWithCapability() {
mockVibrators(1, 2, 3);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL);
@@ -276,22 +422,264 @@ public class VibratorManagerServiceTest {
}
@Test
- public void vibrate_isUnsupported() {
+ public void vibrate_withRingtone_usesRingtoneSettings() throws Exception {
+ mockVibrators(1);
+ mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+
+ setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
+ setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
+ VibratorManagerService service = createService();
+ vibrate(service, VibrationEffect.createOneShot(40, 1), RINGTONE_ATTRS);
+
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
+ setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
+ service = createService();
+ vibrate(service, VibrationEffect.createOneShot(40, 10), RINGTONE_ATTRS);
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+ setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
+ service = createService();
+ vibrate(service, VibrationEffect.createOneShot(40, 100), RINGTONE_ATTRS);
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ assertEquals(2, mVibratorProviders.get(1).getEffects().size());
+ assertEquals(Arrays.asList(10, 100), mVibratorProviders.get(1).getAmplitudes());
+ }
+
+ @Test
+ public void vibrate_withPowerMode_usesPowerModeState() throws Exception {
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createService();
- CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
- VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
- assertExpectException(UnsupportedOperationException.class,
- "Not implemented",
- () -> service.vibrate(UID, PACKAGE_NAME, effect, ALARM_ATTRS, "reason", service));
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+ vibrate(service, VibrationEffect.createOneShot(1, 1), HAPTIC_FEEDBACK_ATTRS);
+ vibrate(service, VibrationEffect.createOneShot(2, 2), RINGTONE_ATTRS);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 1,
+ service, TEST_TIMEOUT_MILLIS));
+
+ mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
+ vibrate(service, VibrationEffect.createOneShot(3, 3), /* attributes= */ null);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 2,
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, VibrationEffect.createOneShot(4, 4), NOTIFICATION_ATTRS);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 3,
+ service, TEST_TIMEOUT_MILLIS));
+
+ assertEquals(Arrays.asList(2, 3, 4), fakeVibrator.getAmplitudes());
}
@Test
- public void cancelVibrate_isUnsupported() {
+ public void vibrate_withAudioAttributes_usesOriginalAudioUsageInAppOpsManager() {
VibratorManagerService service = createService();
- CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
- VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
- assertExpectException(UnsupportedOperationException.class,
- "Not implemented", () -> service.cancelVibrate(service));
+
+ VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ AudioAttributes audioAttributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY).build();
+ VibrationAttributes vibrationAttributes = new VibrationAttributes.Builder(
+ audioAttributes, effect).build();
+
+ vibrate(service, effect, vibrationAttributes);
+
+ verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY), anyInt(), anyString());
+ }
+
+ @Test
+ public void vibrate_withVibrationAttributes_usesCorrespondingAudioUsageInAppOpsManager() {
+ VibratorManagerService service = createService();
+
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), NOTIFICATION_ATTRS);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_COMMUNICATION_REQUEST).build());
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_UNKNOWN).build());
+
+ InOrder inOrderVerifier = inOrder(mAppOpsManagerMock);
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_ALARM), anyInt(), anyString());
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_NOTIFICATION), anyInt(), anyString());
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_NOTIFICATION_RINGTONE), anyInt(), anyString());
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION), anyInt(), anyString());
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST),
+ anyInt(), anyString());
+ inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
+ }
+
+ @Test
+ public void vibrate_withInputDevices_vibratesInputDevices() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
+ when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
+ setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
+ VibratorManagerService service = createService();
+
+ // Prebaked vibration will play fallback waveform on input device.
+ ArgumentCaptor<VibrationEffect> captor = ArgumentCaptor.forClass(VibrationEffect.class);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
+ verify(mIInputManagerMock).vibrate(eq(1), captor.capture(), any());
+ assertTrue(captor.getValue() instanceof VibrationEffect.Waveform);
+
+ VibrationEffect[] effects = new VibrationEffect[]{
+ VibrationEffect.createOneShot(100, 128),
+ VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1),
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose(),
+ };
+
+ for (VibrationEffect effect : effects) {
+ vibrate(service, effect, ALARM_ATTRS);
+ verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
+ }
+
+ // VibrationThread will start this vibration async, so wait before checking it never played.
+ assertFalse(waitUntil(s -> !mVibratorProviders.get(1).getEffects().isEmpty(),
+ service, /* timeout= */ 50));
+ }
+
+ @Test
+ public void vibrate_withNativeCallbackTriggered_finishesVibration() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
+ IVibrator.CAP_AMPLITUDE_CONTROL);
+ mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ VibratorManagerService service = createService();
+ // The native callback will be dispatched manually in this test.
+ mTestLooper.stopAutoDispatchAndIgnoreExceptions();
+
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait before triggering callbacks.
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ // Trigger callbacks from controller.
+ mTestLooper.moveTimeForward(50);
+ mTestLooper.dispatchAll();
+
+ // VibrationThread needs some time to react to native callbacks and stop the vibrator.
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+ }
+
+
+ @Test
+ public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception {
+ mVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_HIGH);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_LOW);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
+ IVibrator.CAP_COMPOSE_EFFECTS);
+ fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ VibratorManagerService service = createService();
+
+ vibrate(service, CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .combine(), ALARM_ATTRS);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 1,
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, CombinedVibrationEffect.startSequential()
+ .addNext(1, VibrationEffect.createOneShot(20, 100))
+ .combine(), NOTIFICATION_ATTRS);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 2,
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .compose(), HAPTIC_FEEDBACK_ATTRS);
+ assertTrue(waitUntil(s -> fakeVibrator.getEffects().size() == 3,
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
+
+ assertEquals(3, fakeVibrator.getEffects().size());
+ assertEquals(1, fakeVibrator.getAmplitudes().size());
+
+ // Alarm vibration is always VIBRATION_INTENSITY_HIGH.
+ VibrationEffect expected = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK, false,
+ VibrationEffect.EFFECT_STRENGTH_STRONG);
+ assertEquals(expected, fakeVibrator.getEffects().get(0));
+
+ // Notification vibrations will be scaled with SCALE_VERY_HIGH.
+ assertTrue(150 < fakeVibrator.getAmplitudes().get(0));
+
+ // Haptic feedback vibrations will be scaled with SCALE_LOW.
+ VibrationEffect.Composed played =
+ (VibrationEffect.Composed) fakeVibrator.getEffects().get(2);
+ assertTrue(0.5 < played.getPrimitiveEffects().get(0).scale);
+ assertTrue(0.5 > played.getPrimitiveEffects().get(1).scale);
+
+ // Ring vibrations have intensity OFF and are not played.
+ }
+
+ @Test
+ public void vibrate_withPowerModeChange_cancelVibrationIfNotAllowed() throws Exception {
+ mockVibrators(1, 2);
+ VibratorManagerService service = createService();
+ vibrate(service,
+ CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
+ .combine(),
+ HAPTIC_FEEDBACK_ATTRS);
+
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+
+ // Haptic feedback cancelled on low power mode.
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+ }
+
+ @Test
+ public void vibrate_withSettingsChange_doNotCancelVibration() throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createService();
+
+ vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ service.updateServiceState();
+ // Vibration is not stopped nearly after updating service.
+ assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50));
+ }
+
+ @Test
+ public void cancelVibrate_stopsVibrating() throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createService();
+
+ service.cancelVibrate(service);
+ assertFalse(service.isVibrating(1));
+
+ vibrate(service, VibrationEffect.createOneShot(10_000, 100), ALARM_ATTRS);
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ service.cancelVibrate(service);
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
}
private void mockVibrators(int... vibratorIds) {
@@ -302,8 +690,56 @@ public class VibratorManagerServiceTest {
when(mNativeWrapperMock.getVibratorIds()).thenReturn(vibratorIds);
}
+ private IVibratorStateListener mockVibratorStateListener() {
+ IVibratorStateListener listenerMock = mock(IVibratorStateListener.class);
+ IBinder binderMock = mock(IBinder.class);
+ when(listenerMock.asBinder()).thenReturn(binderMock);
+ return listenerMock;
+ }
+
+ private InputDevice createInputDeviceWithVibrator(int id) {
+ return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
+ null, /* hasVibrator= */ true, false, false, false, false);
+ }
+
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
LocalServices.removeServiceForTest(clazz);
LocalServices.addService(clazz, mock);
}
+
+ private void setRingerMode(int ringerMode) {
+ AudioManager audioManager = mContextSpy.getSystemService(AudioManager.class);
+ audioManager.setRingerModeInternal(ringerMode);
+ assertEquals(ringerMode, audioManager.getRingerModeInternal());
+ }
+
+ private void setUserSetting(String settingName, int value) {
+ Settings.System.putIntForUser(
+ mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
+ }
+
+ private void setGlobalSetting(String settingName, int value) {
+ Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
+ }
+
+ private void vibrate(VibratorManagerService service, VibrationEffect effect,
+ VibrationAttributes attrs) {
+ vibrate(service, CombinedVibrationEffect.createSynced(effect), attrs);
+ }
+
+ private void vibrate(VibratorManagerService service, CombinedVibrationEffect effect,
+ VibrationAttributes attrs) {
+ service.vibrate(UID, PACKAGE_NAME, effect, attrs, "some reason", service);
+ }
+
+ private boolean waitUntil(Predicate<VibratorManagerService> predicate,
+ VibratorManagerService service, long timeout) throws InterruptedException {
+ long timeoutTimestamp = SystemClock.uptimeMillis() + timeout;
+ boolean predicateResult = false;
+ while (!predicateResult && SystemClock.uptimeMillis() < timeoutTimestamp) {
+ Thread.sleep(10);
+ predicateResult = predicate.test(service);
+ }
+ return predicateResult;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 743848c2453c..2a7905a451b9 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -582,16 +582,19 @@ public class VibratorServiceTest {
VibratorService service = createService();
service.registerVibratorStateListener(mVibratorStateListenerMock);
- verify(mVibratorStateListenerMock).onVibrating(false);
- vibrate(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS);
+ vibrate(service, VibrationEffect.createOneShot(30, 100), ALARM_ATTRS);
// VibrationThread will start this vibration async, so wait before triggering callbacks.
Thread.sleep(10);
+ assertTrue(service.isVibrating());
+
service.unregisterVibratorStateListener(mVibratorStateListenerMock);
// Trigger callbacks from controller.
- mTestLooper.moveTimeForward(150);
+ mTestLooper.moveTimeForward(50);
mTestLooper.dispatchAll();
+ Thread.sleep(20);
+ assertFalse(service.isVibrating());
InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock);
// First notification done when listener is registered.
@@ -745,7 +748,8 @@ public class VibratorServiceTest {
private InputDevice createInputDeviceWithVibrator(int id) {
return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
- null, /* hasVibrator= */ true, false, false, false /* hasSensor */);
+ null, /* hasVibrator= */ true, false, false, false /* hasSensor */,
+ false /* hasBattery */);
}
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/FontCrashDetectorTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/FontCrashDetectorTest.java
new file mode 100644
index 000000000000..275e7c7fec04
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/FontCrashDetectorTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.graphics.fonts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class FontCrashDetectorTest {
+
+ private File mCacheDir;
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mCacheDir = new File(context.getCacheDir(), "UpdatableFontDirTest");
+ FileUtils.deleteContentsAndDir(mCacheDir);
+ mCacheDir.mkdirs();
+ }
+
+ @Test
+ public void detectCrash() throws Exception {
+ // Prepare a marker file.
+ File file = new File(mCacheDir, "detectCrash");
+ assertThat(file.createNewFile()).isTrue();
+
+ FontCrashDetector detector = new FontCrashDetector(file);
+ assertThat(detector.hasCrashed()).isTrue();
+
+ detector.clear();
+ assertThat(detector.hasCrashed()).isFalse();
+ assertThat(file.exists()).isFalse();
+ }
+
+ @Test
+ public void monitorCrash() {
+ File file = new File(mCacheDir, "monitorCrash");
+ FontCrashDetector detector = new FontCrashDetector(file);
+ assertThat(detector.hasCrashed()).isFalse();
+
+ FontCrashDetector.MonitoredBlock block = detector.start();
+ assertThat(file.exists()).isTrue();
+
+ block.close();
+ assertThat(file.exists()).isFalse();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index d06779048837..833103142ccf 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -147,6 +147,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dirForPreparation.loadFontFileMap();
assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
.isEqualTo(expectedModifiedDate);
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
@@ -162,6 +163,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(3);
assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -177,6 +179,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).isEmpty();
}
@@ -187,6 +190,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dirForPreparation.loadFontFileMap();
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -199,6 +203,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).isEmpty();
// All font dirs (including dir for "bar.ttf") should be deleted.
assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
@@ -211,6 +216,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dirForPreparation.loadFontFileMap();
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -224,6 +230,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).isEmpty();
// All font dirs (including dir for "bar.ttf") should be deleted.
assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
@@ -236,6 +243,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dirForPreparation.loadFontFileMap();
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -250,6 +258,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
// For foo.ttf, preinstalled font (revision 5) should be used.
assertThat(dir.getFontFileMap()).doesNotContainKey("foo.ttf");
// For bar.ttf, updated font (revision 4) should be used.
@@ -268,6 +277,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
new File("/dev/null"));
+ dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).isEmpty();
}
@@ -278,6 +288,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
installFontFile(dir, "test,1", GOOD_SIGNATURE);
assertThat(dir.getFontFileMap()).containsKey("test.ttf");
@@ -295,6 +306,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
installFontFile(dir, "test,1", GOOD_SIGNATURE);
Map<String, File> mapBeforeUpgrade = dir.getFontFileMap();
@@ -313,6 +325,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
installFontFile(dir, "test,2", GOOD_SIGNATURE);
try {
@@ -333,6 +346,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
installFontFile(dir, "foo,1", GOOD_SIGNATURE);
installFontFile(dir, "bar,2", GOOD_SIGNATURE);
@@ -349,6 +363,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "test,1", "Invalid signature");
@@ -368,6 +383,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "test,1", GOOD_SIGNATURE);
@@ -398,6 +414,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
readonlyFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "test,2", GOOD_SIGNATURE);
@@ -429,6 +446,7 @@ public final class UpdatableFontDirTest {
return 0;
}
}, fakeFsverityUtil, mConfigFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "foo,1", GOOD_SIGNATURE);
@@ -456,6 +474,7 @@ public final class UpdatableFontDirTest {
return 0;
}
}, fakeFsverityUtil, mConfigFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "foo,1", GOOD_SIGNATURE);
@@ -491,6 +510,7 @@ public final class UpdatableFontDirTest {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
+ dir.loadFontFileMap();
try {
installFontFile(dir, "foo,1", GOOD_SIGNATURE);
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
index fba36cb402a0..c28292f03357 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -18,6 +18,9 @@ package com.android.server.job;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
+import static org.junit.Assert.fail;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.provider.DeviceConfig;
import android.util.Pair;
@@ -32,6 +35,7 @@ import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@@ -58,42 +62,90 @@ public class WorkTypeConfigTest {
}
private void check(@Nullable DeviceConfig.Properties config,
- int defaultTotal, int defaultMaxBg, int defaultMinBg,
- int expectedTotal, int expectedMaxBg, int expectedMinBg) throws Exception {
+ int defaultTotal,
+ @Nullable Pair<Integer, Integer> defaultTopLimits,
+ @Nullable Pair<Integer, Integer> defaultBgLimits,
+ boolean expectedValid, int expectedTotal,
+ @NonNull Pair<Integer, Integer> expectedTopLimits,
+ @NonNull Pair<Integer, Integer> expectedBgLimits) throws Exception {
resetConfig();
if (config != null) {
DeviceConfig.setProperties(config);
}
- final WorkTypeConfig counts = new WorkTypeConfig("test",
- defaultTotal,
- // defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, defaultTotal - defaultMaxBg),
- Pair.create(WORK_TYPE_BG, defaultMinBg)),
- // defaultMax
- List.of(Pair.create(WORK_TYPE_BG, defaultMaxBg)));
+ List<Pair<Integer, Integer>> defaultMin = new ArrayList<>();
+ List<Pair<Integer, Integer>> defaultMax = new ArrayList<>();
+ Integer val;
+ if (defaultTopLimits != null) {
+ if ((val = defaultTopLimits.first) != null) {
+ defaultMin.add(Pair.create(WORK_TYPE_TOP, val));
+ }
+ if ((val = defaultTopLimits.second) != null) {
+ defaultMax.add(Pair.create(WORK_TYPE_TOP, val));
+ }
+ }
+ if (defaultBgLimits != null) {
+ if ((val = defaultBgLimits.first) != null) {
+ defaultMin.add(Pair.create(WORK_TYPE_BG, val));
+ }
+ if ((val = defaultBgLimits.second) != null) {
+ defaultMax.add(Pair.create(WORK_TYPE_BG, val));
+ }
+ }
+
+ final WorkTypeConfig counts;
+ try {
+ counts = new WorkTypeConfig("test",
+ defaultTotal, defaultMin, defaultMax);
+ if (!expectedValid) {
+ fail("Invalid config successfully created");
+ return;
+ }
+ } catch (IllegalArgumentException e) {
+ if (expectedValid) {
+ throw e;
+ } else {
+ // Success
+ return;
+ }
+ }
counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
Assert.assertEquals(expectedTotal, counts.getMaxTotal());
- Assert.assertEquals(expectedMaxBg, counts.getMax(WORK_TYPE_BG));
- Assert.assertEquals(expectedMinBg, counts.getMinReserved(WORK_TYPE_BG));
+ Assert.assertEquals((int) expectedTopLimits.first, counts.getMinReserved(WORK_TYPE_TOP));
+ Assert.assertEquals((int) expectedTopLimits.second, counts.getMax(WORK_TYPE_TOP));
+ Assert.assertEquals((int) expectedBgLimits.first, counts.getMinReserved(WORK_TYPE_BG));
+ Assert.assertEquals((int) expectedBgLimits.second, counts.getMax(WORK_TYPE_BG));
}
@Test
public void test() throws Exception {
// Tests with various combinations.
- check(null, /*default*/ 5, 1, 0, /*expected*/ 5, 1, 0);
- check(null, /*default*/ 5, 0, 0, /*expected*/ 5, 1, 0);
- check(null, /*default*/ 0, 0, 0, /*expected*/ 1, 1, 0);
- check(null, /*default*/ -1, -1, -1, /*expected*/ 1, 1, 0);
- check(null, /*default*/ 5, 5, 5, /*expected*/ 5, 5, 4);
- check(null, /*default*/ 6, 5, 6, /*expected*/ 6, 5, 5);
- check(null, /*default*/ 4, 5, 6, /*expected*/ 4, 4, 3);
- check(null, /*default*/ 5, 1, 1, /*expected*/ 5, 1, 1);
- check(null, /*default*/ 15, 15, 15, /*expected*/ 15, 15, 14);
- check(null, /*default*/ 16, 16, 16, /*expected*/ 16, 16, 15);
- check(null, /*default*/ 20, 20, 20, /*expected*/ 16, 16, 15);
+ check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1),
+ /*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1));
+ check(null, /*default*/ 5, Pair.create(5, null), Pair.create(0, 0),
+ /*expected*/ true, 5, Pair.create(5, 5), Pair.create(0, 1));
+ check(null, /*default*/ 0, Pair.create(5, null), Pair.create(0, 0),
+ /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
+ check(null, /*default*/ -1, null, Pair.create(-1, -1),
+ /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
+ check(null, /*default*/ 5, null, Pair.create(5, 5),
+ /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
+ check(null, /*default*/ 6, Pair.create(1, null), Pair.create(6, 5),
+ /*expected*/ false, 6, Pair.create(1, 6), Pair.create(5, 5));
+ check(null, /*default*/ 4, null, Pair.create(6, 5),
+ /*expected*/ false, 4, Pair.create(1, 4), Pair.create(3, 4));
+ check(null, /*default*/ 5, Pair.create(4, null), Pair.create(1, 1),
+ /*expected*/ true, 5, Pair.create(4, 5), Pair.create(1, 1));
+ check(null, /*default*/ 15, null, Pair.create(15, 15),
+ /*expected*/ true, 15, Pair.create(1, 15), Pair.create(14, 15));
+ check(null, /*default*/ 16, null, Pair.create(16, 16),
+ /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
+ check(null, /*default*/ 20, null, Pair.create(20, 20),
+ /*expected*/ false, 16, Pair.create(1, 16), Pair.create(15, 16));
+ check(null, /*default*/ 20, null, Pair.create(16, 16),
+ /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
// Test for overriding with a setting string.
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
@@ -101,15 +153,26 @@ public class WorkTypeConfigTest {
.setInt(KEY_MAX_BG, 4)
.setInt(KEY_MIN_BG, 3)
.build(),
- /*default*/ 9, 9, 9, /*expected*/ 5, 4, 3);
+ /*default*/ 9, null, Pair.create(9, 9),
+ /*expected*/ true, 5, Pair.create(1, 5), Pair.create(3, 4));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_TOTAL, 5).build(),
- /*default*/ 9, 9, 9, /*expected*/ 5, 5, 4);
+ /*default*/ 9, null, Pair.create(9, 9),
+ /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_BG, 4).build(),
- /*default*/ 9, 9, 9, /*expected*/ 9, 4, 4);
+ /*default*/ 9, null, Pair.create(9, 9),
+ /*expected*/ true, 9, Pair.create(1, 9), Pair.create(4, 4));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MIN_BG, 3).build(),
- /*default*/ 9, 9, 9, /*expected*/ 9, 9, 3);
+ /*default*/ 9, null, Pair.create(9, 9),
+ /*expected*/ true, 9, Pair.create(1, 9), Pair.create(3, 9));
+ check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+ .setInt(KEY_MAX_TOTAL, 20)
+ .setInt(KEY_MAX_BG, 20)
+ .setInt(KEY_MIN_BG, 8)
+ .build(),
+ /*default*/ 9, null, Pair.create(9, 9),
+ /*expected*/ true, 16, Pair.create(1, 16), Pair.create(8, 16));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 161d3163c1cf..6e5789686c55 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -535,8 +535,11 @@ public final class DataManagerTest {
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
- assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
- TEST_SHORTCUT_ID)).isNotNull();
+ ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID);
+ assertThat(result).isNotNull();
+ assertThat(result.hasBirthdayToday()).isFalse();
+ assertThat(result.getStatuses()).isEmpty();
}
@Test
@@ -550,13 +553,15 @@ public final class DataManagerTest {
shortcut.setCached(ShortcutInfo.FLAG_PINNED);
mDataManager.addOrUpdateConversationInfo(shortcut);
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
- TEST_SHORTCUT_ID)).isNotNull();
+ TEST_SHORTCUT_ID)).isNotNull();
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID + "1")).isNull();
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
listenerService.onNotificationPosted(mStatusBarNotification);
+ ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID);
@@ -568,6 +573,8 @@ public final class DataManagerTest {
result.getParentNotificationChannel().getId());
assertEquals(mStatusBarNotification.getPostTime(), result.getLastEventTimestamp());
assertTrue(result.hasActiveNotifications());
+ assertFalse(result.hasBirthdayToday());
+ assertThat(result.getStatuses()).containsExactly(cs);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
index 28d313b4d4b5..e71c2f5ba8da 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
@@ -294,6 +294,8 @@ public class InputDeviceDelegateTest {
private InputDevice createInputDevice(int id, boolean hasVibrator) {
return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
- null, hasVibrator, false, false, false /* hasSensor */);
+ null, hasVibrator, false, false, false /* hasSensor */, false /* hasBattery */);
+
+
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 5c67db7ef813..91fd7a2ad0c1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -16,8 +16,8 @@
package com.android.server.wm;
-import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
@@ -50,6 +50,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
@@ -408,6 +409,32 @@ public class DisplayAreaTest extends WindowTestsBase {
}
@Test
+ public void testRestrictAppBoundsToOverrideBounds() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm);
+ final DisplayArea<DisplayArea> da = new DisplayArea<>(mWm, ANY, "Test_DA");
+ root.addChild(da, POSITION_TOP);
+ final Rect displayBounds = new Rect(0, 0, 1800, 2800);
+ final Rect displayAppBounds = new Rect(0, 100, 1800, 2800);
+ final Rect daBounds = new Rect(0, 1400, 1800, 2800);
+ root.setBounds(displayBounds);
+
+ // DA inherit parent app bounds.
+ final Configuration displayConfig = new Configuration();
+ displayConfig.windowConfiguration.setAppBounds(displayAppBounds);
+ root.onRequestedOverrideConfigurationChanged(displayConfig);
+
+ assertEquals(displayAppBounds, da.getConfiguration().windowConfiguration.getAppBounds());
+
+ // Restrict DA appBounds to override Bounds
+ da.setBounds(daBounds);
+
+ final Rect expectedDaAppBounds = new Rect(daBounds);
+ expectedDaAppBounds.intersect(displayAppBounds);
+ assertEquals(expectedDaAppBounds, da.getConfiguration().windowConfiguration.getAppBounds());
+ }
+
+ @Test
public void testGetOrientation() {
final DisplayArea.Tokens area = new DisplayArea.Tokens(mWm, ABOVE_TASKS, "test");
final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 82ffa765cc27..37fb0e930acc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -123,6 +123,15 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
updateDisplayFrames();
}
+ void addWindowWithRawInsetsState(WindowState win) {
+ addWindow(win);
+ // Without mPerformLayout in display content, the window cannot see any insets. Override the
+ // insets state with the global one.
+ final InsetsState insetsState =
+ win.getDisplayContent().getInsetsStateController().getRawInsetsState();
+ win.mAboveInsetsState = insetsState;
+ }
+
public void setRotation(int rotation, boolean includingWindows) {
mRotation = rotation;
updateDisplayFrames();
@@ -272,7 +281,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_fitStatusBars() {
mWindow.mAttrs.setFitInsetsTypes(Type.statusBars());
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -283,7 +292,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_fitNavigationBars() {
mWindow.mAttrs.setFitInsetsTypes(Type.navigationBars());
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -294,7 +303,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_fitAllSides() {
mWindow.mAttrs.setFitInsetsSides(Side.all());
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -305,7 +314,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_fitTopOnly() {
mWindow.mAttrs.setFitInsetsSides(Side.TOP);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -315,11 +324,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_fitInsetsIgnoringVisibility() {
- final InsetsState state = mWindow.getInsetsState();
+ final InsetsState state =
+ mDisplayContent.getInsetsStateController().getRawInsetsState();
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
mWindow.mAttrs.setFitInsetsIgnoringVisibility(true);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -329,11 +339,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_fitInsetsNotIgnoringVisibility() {
- final InsetsState state = mWindow.getInsetsState();
+ final InsetsState state =
+ mDisplayContent.getInsetsStateController().getRawInsetsState();
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
mWindow.mAttrs.setFitInsetsIgnoringVisibility(false);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -349,8 +360,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
state.getSource(InsetsState.ITYPE_IME).setFrame(
0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
- mWindow.mBehindIme = true;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -364,7 +374,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.setFitInsetsTypes(Type.displayCutout());
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -379,7 +389,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -395,7 +405,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -411,7 +421,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -427,7 +437,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -442,7 +452,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -457,11 +467,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
+ mDisplayContent.getInsetsStateController().getRawInsetsState()
+ .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
final InsetsState requestedState = new InsetsState();
requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
mWindow.updateRequestedVisibility(requestedState);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -476,12 +487,13 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
+ mDisplayContent.getInsetsStateController().getRawInsetsState()
+ .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
final InsetsState requestedState = new InsetsState();
requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
mWindow.updateRequestedVisibility(requestedState);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -497,7 +509,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -513,7 +525,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -529,7 +541,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -545,7 +557,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
mWindow.mAttrs.width = DISPLAY_WIDTH;
mWindow.mAttrs.height = DISPLAY_HEIGHT;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -562,7 +574,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -576,7 +588,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -592,7 +604,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -608,7 +620,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -624,7 +636,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -638,7 +650,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING;
- addWindow(mWindow);
+ addWindowWithRawInsetsState(mWindow);
final int forwardedInsetBottom = 50;
mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom));
@@ -776,9 +788,13 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
public void testFixedRotationInsetsSourceFrame() {
doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent)
.rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
- final Rect frame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
+ mWindow.mAboveInsetsState.addSource(mDisplayContent.getInsetsStateController()
+ .getRawInsetsState().peekSource(ITYPE_STATUS_BAR));
+ final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
+ .getSource(ITYPE_STATUS_BAR).getFrame();
mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
- final Rect rotatedFrame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
+ final Rect rotatedFrame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
+ .getSource(ITYPE_STATUS_BAR).getFrame();
assertEquals(DISPLAY_WIDTH, frame.width());
assertEquals(DISPLAY_HEIGHT, rotatedFrame.width());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 77537a9de6be..499507e969cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -300,6 +300,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
+ mImeWindow.mAboveInsetsState = state;
mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(),
state, displayInfo, null /* displayCutout */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
index e6f24da3e7b9..f91c9d0e9853 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index e0fd3796f2aa..bf3ed692dc8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -45,6 +45,7 @@ import static org.mockito.Mockito.verify;
import android.app.StatusBarManager;
import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -272,7 +273,6 @@ public class InsetsPolicyTest extends WindowTestsBase {
final WindowState navBar = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar");
navBar.setHasSurface(true);
navBar.getControllableInsetProvider().setServerVisible(true);
-
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
@@ -337,11 +337,14 @@ public class InsetsPolicyTest extends WindowTestsBase {
@UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testAbortTransientBars_bothCanBeAborted_appGetsBothRealControls() {
- addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
- .getControllableInsetProvider().getSource().setVisible(false);
- addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
- .getControllableInsetProvider().getSource().setVisible(false);
-
+ final InsetsSource statusBarSource = addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
+ .getControllableInsetProvider().getSource();
+ final InsetsSource navBarSource = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
+ .getControllableInsetProvider().getSource();
+ statusBarSource.setVisible(false);
+ navBarSource.setVisible(false);
+ mAppWindow.mAboveInsetsState.addSource(navBarSource);
+ mAppWindow.mAboveInsetsState.addSource(statusBarSource);
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(mAppWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 276643847712..2107ab1eeeea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -59,25 +59,6 @@ import org.junit.runner.RunWith;
public class InsetsStateControllerTest extends WindowTestsBase {
@Test
- public void testStripForDispatch_notOwn() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
- statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
- assertNotNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
- }
-
- @Test
- public void testStripForDispatch_own() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
- .setWindow(statusBar, null, null);
- statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
- final InsetsState state = getController().getInsetsForWindow(statusBar);
- assertNull(state.peekSource(ITYPE_STATUS_BAR));
- }
-
- @Test
public void testStripForDispatch_navBar() {
final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
@@ -142,14 +123,15 @@ public class InsetsStateControllerTest extends WindowTestsBase {
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
- app1.mBehindIme = true;
-
final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
- app2.mBehindIme = false;
+
+ app1.mAboveInsetsState.addSource(getController().getRawInsetsState().getSource(ITYPE_IME));
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
- assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME).isVisible());
- assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME)
+ .isVisible());
+ assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME)
+ .isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -158,7 +140,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- app.mBehindIme = true;
+ app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true);
+ app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
@@ -170,10 +153,10 @@ public class InsetsStateControllerTest extends WindowTestsBase {
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- app.mBehindIme = false;
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
- assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
+ .isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -210,7 +193,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
// app won't get visible IME insets while above IME even when IME is visible.
assertTrue(getController().getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
- assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
+ .isVisible());
// Reset invocation counter.
clearInvocations(app);
@@ -219,6 +203,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
app.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
mDisplayContent.computeImeTarget(true);
mDisplayContent.applySurfaceChangesTransaction();
+ app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true);
+ app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
// Make sure app got notified.
verify(app, atLeast(1)).notifyInsetsChanged();
@@ -234,6 +220,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+ app.mAboveInsetsState.set(getController().getRawInsetsState());
+ child.mAboveInsetsState.set(getController().getRawInsetsState());
child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
mDisplayContent.computeImeTarget(true);
@@ -242,7 +230,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
- assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
+ .isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -252,6 +241,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+ app.mAboveInsetsState.addSource(getController().getRawInsetsState().peekSource(ITYPE_IME));
child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
@@ -261,7 +251,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
- assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
+ .isVisible());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index d0d612fc8dda..bbd89b8d0173 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -26,6 +26,7 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -480,10 +481,13 @@ public class SizeCompatTests extends WindowTestsBase {
/**
* Ensures that {@link TaskStackListener} can receive callback about the activity in size
* compatibility mode.
+ *
+ * TODO(b/178327644) Remove after update DC#handleActivitySizeCompatModeIfNeeded
*/
@Test
public void testHandleActivitySizeCompatMode() {
setUpDisplaySizeWithApp(1000, 2000);
+ doReturn(true).when(mTask).isOrganized();
ActivityRecord activity = mActivity;
activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -520,6 +524,46 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(null, compatTokens.get(0));
}
+ /**
+ * Ensures that {@link TaskOrganizerController} can receive callback about the activity in size
+ * compatibility mode.
+ */
+ @Test
+ public void testHandleActivitySizeCompatModeChanged() {
+ setUpDisplaySizeWithApp(1000, 2000);
+ doReturn(true).when(mTask).isOrganized();
+ ActivityRecord activity = mActivity;
+ activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ assertFitted();
+
+ // Resize the display so that the activity exercises size-compat mode.
+ resizeDisplay(mTask.mDisplayContent, 1000, 2500);
+
+ // Expect the exact token when the activity is in size compatibility mode.
+ verify(mTask).onSizeCompatActivityChanged();
+ ActivityManager.RunningTaskInfo taskInfo = mTask.getTaskInfo();
+
+ assertEquals(mActivity.appToken, taskInfo.topActivityToken);
+ assertTrue(taskInfo.topActivityInSizeCompat);
+
+ // Make the activity resizable again by restarting it
+ clearInvocations(mTask);
+ activity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+ activity.mVisibleRequested = true;
+ activity.restartProcessIfVisible();
+ // The full lifecycle isn't hooked up so manually set state to resumed
+ activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);
+
+ // Expect null token when switching to non-size-compat mode activity.
+ verify(mTask).onSizeCompatActivityChanged();
+ taskInfo = mTask.getTaskInfo();
+
+ assertEquals(mActivity.appToken, taskInfo.topActivityToken);
+ assertFalse(taskInfo.topActivityInSizeCompat);
+ }
+
@Test
public void testShouldUseSizeCompatModeOnResizableTask() {
setUpDisplaySizeWithApp(1000, 2500);
@@ -602,12 +646,11 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
mActivity.getLetterboxInsets());
- final BarController statusBarController =
- mActivity.mDisplayContent.getDisplayPolicy().getStatusBarController();
+ final DisplayPolicy displayPolicy = mActivity.mDisplayContent.getDisplayPolicy();
// The activity doesn't fill the display, so the letterbox of the rotated activity is
// overlapped with the rotated content frame of status bar. Hence the status bar shouldn't
// be transparent.
- assertFalse(statusBarController.isFullyTransparentAllowed(w));
+ assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
// Make the activity fill the display.
prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
@@ -617,7 +660,7 @@ public class SizeCompatTests extends WindowTestsBase {
// The letterbox should only cover the notch area, so status bar can be transparent.
assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
- assertTrue(statusBarController.isFullyTransparentAllowed(w));
+ assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
}
@Test
@@ -977,9 +1020,9 @@ public class SizeCompatTests extends WindowTestsBase {
displayPolicy.onConfigurationChanged();
final TestWindowToken token = createTestWindowToken(
- WindowManager.LayoutParams.TYPE_STATUS_BAR, displayContent);
+ TYPE_STATUS_BAR, displayContent);
final WindowManager.LayoutParams attrs =
- new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_STATUS_BAR);
+ new WindowManager.LayoutParams(TYPE_STATUS_BAR);
attrs.gravity = android.view.Gravity.TOP;
attrs.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 1607f013ee81..a1f89ec75784 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -275,7 +275,7 @@ public class WindowFrameTests extends WindowTestsBase {
imeSource.setFrame(imeFrame);
imeSource.setVisible(true);
w.updateRequestedVisibility(state);
- w.mBehindIme = true;
+ w.mAboveInsetsState.addSource(imeSource);
// With no insets or system decor all the frames incoming from PhoneWindowManager
// are identical.
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 37983b442425..77fca3d2fdeb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -42,6 +42,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
+import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
@@ -55,6 +56,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
@@ -1244,6 +1246,54 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertEquals(splitPrimaryRootTask, activity.getRootTask());
}
+ @Test
+ public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task rootTask = createStack();
+ final Task task = createTask(rootTask);
+ final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task);
+ final ArgumentCaptor<RunningTaskInfo> infoCaptor =
+ ArgumentCaptor.forClass(RunningTaskInfo.class);
+
+ assertTrue(rootTask.isOrganized());
+
+ spyOn(activity);
+ doReturn(true).when(activity).inSizeCompatMode();
+ doReturn(true).when(activity).isState(RESUMED);
+
+ // Ensure task info show top activity in size compat.
+ rootTask.onSizeCompatActivityChanged();
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+ verify(organizer).onTaskInfoChanged(infoCaptor.capture());
+ RunningTaskInfo info = infoCaptor.getValue();
+ assertEquals(rootTask.mTaskId, info.taskId);
+ assertEquals(activity.appToken, info.topActivityToken);
+ assertTrue(info.topActivityInSizeCompat);
+
+ // Ensure task info show top activity that is not in foreground as not in size compat.
+ clearInvocations(organizer);
+ doReturn(false).when(activity).isState(RESUMED);
+ rootTask.onSizeCompatActivityChanged();
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+ verify(organizer).onTaskInfoChanged(infoCaptor.capture());
+ info = infoCaptor.getValue();
+ assertEquals(rootTask.mTaskId, info.taskId);
+ assertEquals(activity.appToken, info.topActivityToken);
+ assertFalse(info.topActivityInSizeCompat);
+
+ // Ensure task info show non size compat top activity as not in size compat.
+ clearInvocations(organizer);
+ doReturn(true).when(activity).isState(RESUMED);
+ doReturn(false).when(activity).inSizeCompatMode();
+ rootTask.onSizeCompatActivityChanged();
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+ verify(organizer).onTaskInfoChanged(infoCaptor.capture());
+ info = infoCaptor.getValue();
+ assertEquals(rootTask.mTaskId, info.taskId);
+ assertEquals(activity.appToken, info.topActivityToken);
+ assertFalse(info.topActivityInSizeCompat);
+ }
+
/**
* Verifies that task vanished is called for a specific task.
*/
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 3815326b6886..0cb1255c6830 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -79,7 +79,6 @@ import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -111,6 +110,7 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
/**
* A service that collects, aggregates, and persists application usage data.
@@ -162,7 +162,7 @@ public class UsageStatsService extends SystemService implements
ShortcutServiceInternal mShortcutServiceInternal;
private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
- private final SparseBooleanArray mUserUnlockedStates = new SparseBooleanArray();
+ private final CopyOnWriteArraySet<Integer> mUserUnlockedStates = new CopyOnWriteArraySet<>();
private final SparseIntArray mUidToKernelCounter = new SparseIntArray();
int mUsageSource;
@@ -333,7 +333,7 @@ public class UsageStatsService extends SystemService implements
synchronized (mLock) {
// User was started but never unlocked so no need to report a user stopped event
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
persistPendingEventsLocked(userId);
return;
}
@@ -346,7 +346,7 @@ public class UsageStatsService extends SystemService implements
if (userService != null) {
userService.userStopped();
}
- mUserUnlockedStates.put(userId, false);
+ mUserUnlockedStates.remove(userId);
mUserState.put(userId, null); // release the service (mainly for GC)
}
}
@@ -360,6 +360,11 @@ public class UsageStatsService extends SystemService implements
UsageStatsIdleService.scheduleUpdateMappingsJob(getContext());
}
synchronized (mLock) {
+ // This should be safe to add this early. Other than reportEventOrAddToQueue, every
+ // other user grabs the lock before accessing
+ // mUserUnlockedStates. reportEventOrAddToQueue does not depend on anything other than
+ // mUserUnlockedStates, and the lock will protect the handler.
+ mUserUnlockedStates.add(userId);
// Create a user unlocked event to report
final Event unlockEvent = new Event(USER_UNLOCKED, SystemClock.elapsedRealtime());
unlockEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
@@ -377,7 +382,6 @@ public class UsageStatsService extends SystemService implements
initializeUserUsageStatsServiceLocked(userId, System.currentTimeMillis(),
installedPackages);
- mUserUnlockedStates.put(userId, true);
final UserUsageStatsService userService = getUserUsageStatsServiceLocked(userId);
if (userService == null) {
Slog.i(TAG, "Attempted to unlock stopped or removed user " + userId);
@@ -780,12 +784,11 @@ public class UsageStatsService extends SystemService implements
}
private void reportEventOrAddToQueue(int userId, Event event) {
+ if (mUserUnlockedStates.contains(userId)) {
+ mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
+ return;
+ }
synchronized (mLock) {
- if (mUserUnlockedStates.get(userId)) {
- mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
- return;
- }
-
LinkedList<Event> events = mReportedEvents.get(userId);
if (events == null) {
events = new LinkedList<>();
@@ -823,7 +826,7 @@ public class UsageStatsService extends SystemService implements
synchronized (mLock) {
// This should never be called directly when the user is locked
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.wtf(TAG, "Failed to report event for locked user " + userId
+ " (" + event.mPackage + "/" + event.mClass
+ " eventType:" + event.mEventType
@@ -1006,7 +1009,7 @@ public class UsageStatsService extends SystemService implements
final int tokenRemoved;
synchronized (mLock) {
final long timeRemoved = System.currentTimeMillis();
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
// If user is not unlocked and a package is removed for them, we will handle it
// when the user service is initialized and package manager is queried.
return;
@@ -1030,7 +1033,7 @@ public class UsageStatsService extends SystemService implements
*/
private boolean pruneUninstalledPackagesData(int userId) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
return false; // user is no longer unlocked
}
@@ -1050,7 +1053,7 @@ public class UsageStatsService extends SystemService implements
// fetch the installed packages outside the lock so it doesn't block package manager.
final HashMap<String, Long> installedPkgs = getInstalledPackages(UserHandle.USER_SYSTEM);
synchronized (mLock) {
- if (!mUserUnlockedStates.get(UserHandle.USER_SYSTEM)) {
+ if (!mUserUnlockedStates.contains(UserHandle.USER_SYSTEM)) {
return false; // user is no longer unlocked
}
@@ -1069,7 +1072,7 @@ public class UsageStatsService extends SystemService implements
List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime,
boolean obfuscateInstantApps) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.w(TAG, "Failed to query usage stats for locked user " + userId);
return null;
}
@@ -1103,7 +1106,7 @@ public class UsageStatsService extends SystemService implements
List<ConfigurationStats> queryConfigurationStats(int userId, int bucketType, long beginTime,
long endTime) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.w(TAG, "Failed to query configuration stats for locked user " + userId);
return null;
}
@@ -1122,7 +1125,7 @@ public class UsageStatsService extends SystemService implements
List<EventStats> queryEventStats(int userId, int bucketType, long beginTime,
long endTime) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.w(TAG, "Failed to query event stats for locked user " + userId);
return null;
}
@@ -1140,7 +1143,7 @@ public class UsageStatsService extends SystemService implements
*/
UsageEvents queryEvents(int userId, long beginTime, long endTime, int flags) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.w(TAG, "Failed to query events for locked user " + userId);
return null;
}
@@ -1159,7 +1162,7 @@ public class UsageStatsService extends SystemService implements
UsageEvents queryEventsForPackage(int userId, long beginTime, long endTime,
String packageName, boolean includeTaskRoot) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
Slog.w(TAG, "Failed to query package events for locked user " + userId);
return null;
}
@@ -1203,7 +1206,7 @@ public class UsageStatsService extends SystemService implements
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
final int userId = mUserState.keyAt(i);
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
persistPendingEventsLocked(userId);
continue;
}
@@ -1261,7 +1264,7 @@ public class UsageStatsService extends SystemService implements
final int numUsers = mUserState.size();
for (int user = 0; user < numUsers; user++) {
final int userId = mUserState.keyAt(user);
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
continue;
}
ipw.println("user=" + userId);
@@ -1288,7 +1291,7 @@ public class UsageStatsService extends SystemService implements
final int numUsers = mUserState.size();
for (int user = 0; user < numUsers; user++) {
final int userId = mUserState.keyAt(user);
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
continue;
}
ipw.println("user=" + userId);
@@ -1344,7 +1347,7 @@ public class UsageStatsService extends SystemService implements
idpw.printPair("user", userId);
idpw.println();
idpw.increaseIndent();
- if (mUserUnlockedStates.get(userId)) {
+ if (mUserUnlockedStates.contains(userId)) {
if (checkin) {
mUserState.valueAt(i).checkin(idpw);
} else {
@@ -1382,7 +1385,7 @@ public class UsageStatsService extends SystemService implements
ipw.println("the specified user does not exist.");
return UserHandle.USER_NULL;
}
- if (!mUserUnlockedStates.get(userId)) {
+ if (!mUserUnlockedStates.contains(userId)) {
ipw.println("the specified user is currently in a locked state.");
return UserHandle.USER_NULL;
}
@@ -2250,12 +2253,11 @@ public class UsageStatsService extends SystemService implements
@Override
public byte[] getBackupPayload(int user, String key) {
+ if (!mUserUnlockedStates.contains(user)) {
+ Slog.w(TAG, "Failed to get backup payload for locked user " + user);
+ return null;
+ }
synchronized (mLock) {
- if (!mUserUnlockedStates.get(user)) {
- Slog.w(TAG, "Failed to get backup payload for locked user " + user);
- return null;
- }
-
// Check to ensure that only user 0's data is b/r for now
// Note: if backup and restore is enabled for users other than the system user, the
// #onUserUnlocked logic, specifically when the update mappings job is scheduled via
@@ -2275,7 +2277,7 @@ public class UsageStatsService extends SystemService implements
@Override
public void applyRestoredPayload(int user, String key, byte[] payload) {
synchronized (mLock) {
- if (!mUserUnlockedStates.get(user)) {
+ if (!mUserUnlockedStates.contains(user)) {
Slog.w(TAG, "Failed to apply restored payload for locked user " + user);
return;
}
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 1faae42f054b..a9dae898fc9f 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -138,6 +138,8 @@ public final class ImsCallProfile implements Parcelable {
* Indicates if the session is for a conference call or not. If not defined, should be
* considered {@code false}.
* Boolean extra properties - {@code true} / {@code false}.
+ *
+ * This extra is set on an instance of {@link ImsCallProfile} via {@link #setCallExtraBoolean}.
* @hide
*/
@SystemApi
@@ -174,6 +176,8 @@ public final class ImsCallProfile implements Parcelable {
* Indicates if the session can be extended to a conference call. If not defined, should be
* considered {@code false}.
* Boolean extra properties - {@code true} / {@code false}.
+ *
+ * This extra is set on an instance of {@link ImsCallProfile} via {@link #setCallExtraBoolean}.
* @hide
*/
@SystemApi
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 22aa18fe34b0..d17415a7354a 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2360,6 +2360,16 @@ interface ITelephony {
*/
String getContactFromEab(String contact);
+ /*
+ * Check whether the device supports RCS User Capability Exchange or not.
+ */
+ boolean getDeviceUceEnabled();
+
+ /*
+ * Set the device supports RCS User Capability Exchange.
+ */
+ void setDeviceUceEnabled(boolean isEnabled);
+
/**
* Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
* specified thresholds.
diff --git a/tests/FlickerTests/TEST_MAPPING b/tests/FlickerTests/TEST_MAPPING
deleted file mode 100644
index db251b907caa..000000000000
--- a/tests/FlickerTests/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "postsubmit": [
- // Run tests on real device
- {
- "name": "FlickerTests",
- "keywords": ["primary-device"]
- },
- // Also run the tests in the cloud
- {
- "name": "FlickerTests"
- }
- ]
-} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index ba12fbed2b2f..6b6d21bc6e54 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -21,9 +21,12 @@ import com.android.server.wm.flicker.dsl.EventLogAssertionBuilder
import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
import com.android.server.wm.flicker.dsl.WmAssertionBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_WINDOW_NAME
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_LAYER_NAME
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_WINDOW_NAME
-const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
-const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
+const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider"
const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
const val WALLPAPER_TITLE = "Wallpaper"
@@ -33,7 +36,7 @@ fun WmAssertionBuilder.statusBarWindowIsAlwaysVisible(
enabled: Boolean = bugId == 0
) {
all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
- this.showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
+ this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
}
}
@@ -43,7 +46,7 @@ fun WmAssertionBuilder.navBarWindowIsAlwaysVisible(
enabled: Boolean = bugId == 0
) {
all("navBarWindowIsAlwaysVisible", bugId, enabled) {
- this.showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
+ this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
}
}
@@ -113,6 +116,18 @@ fun WmAssertionBuilder.appWindowBecomesVisible(
}
}
+fun WmAssertionBuilder.appWindowBecomesInVisible(
+ appName: String,
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("appWindowBecomesInVisible", bugId, enabled) {
+ this.showsAppWindow(appName)
+ .then()
+ .hidesAppWindow(appName)
+ }
+}
+
@JvmOverloads
fun LayersAssertionBuilder.noUncoveredRegions(
beginRotation: Int,
@@ -151,15 +166,15 @@ fun LayersAssertionBuilder.navBarLayerIsAlwaysVisible(
) {
if (rotatesScreen) {
all("navBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ this.showsLayer(NAV_BAR_LAYER_NAME)
.then()
- .hidesLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ .hidesLayer(NAV_BAR_LAYER_NAME)
.then()
- .showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ .showsLayer(NAV_BAR_LAYER_NAME)
}
} else {
all("navBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ this.showsLayer(NAV_BAR_LAYER_NAME)
}
}
}
@@ -172,15 +187,15 @@ fun LayersAssertionBuilder.statusBarLayerIsAlwaysVisible(
) {
if (rotatesScreen) {
all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(STATUS_BAR_WINDOW_TITLE)
+ this.showsLayer(STATUS_BAR_LAYER_NAME)
.then()
- hidesLayer(STATUS_BAR_WINDOW_TITLE)
+ hidesLayer(STATUS_BAR_LAYER_NAME)
.then()
- .showsLayer(STATUS_BAR_WINDOW_TITLE)
+ .showsLayer(STATUS_BAR_LAYER_NAME)
}
} else {
all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(STATUS_BAR_WINDOW_TITLE)
+ this.showsLayer(STATUS_BAR_LAYER_NAME)
}
}
}
@@ -196,15 +211,15 @@ fun LayersAssertionBuilder.navBarLayerRotatesAndScales(
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
start("navBarLayerRotatesAndScales_StartingPos", bugId, enabled) {
- this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
}
end("navBarLayerRotatesAndScales_EndingPost", bugId, enabled) {
- this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
+ this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos)
}
if (startingPos == endingPos) {
all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
- this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
}
}
}
@@ -220,10 +235,10 @@ fun LayersAssertionBuilder.statusBarLayerRotatesScales(
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
start("statusBarLayerRotatesScales_StartingPos", bugId, enabled) {
- this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
+ this.hasVisibleRegion(STATUS_BAR_LAYER_NAME, startingPos)
}
end("statusBarLayerRotatesScales_EndingPos", bugId, enabled) {
- this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos)
+ this.hasVisibleRegion(STATUS_BAR_LAYER_NAME, endingPos)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index b569edac1a95..1a4744980b1e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -41,6 +41,9 @@ fun Flicker.setRotation(rotation: Int) {
wmHelper.waitForRotation(rotation)
wmHelper.waitForNavBarStatusBarVisible()
wmHelper.waitForAppTransitionIdle()
+
+ // Ensure WindowManagerService wait until all animations have completed
+ instrumentation.getUiAutomation().syncInputTransactions()
} catch (e: RemoteException) {
throw RuntimeException(e)
}
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 104758de49f1..5381009fdf2b 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -98,6 +98,8 @@ public class PackageWatchdogTest {
private final TestClock mTestClock = new TestClock();
private TestLooper mTestLooper;
private Context mSpyContext;
+ // Keep track of all created watchdogs to apply device config changes
+ private List<PackageWatchdog> mAllocatedWatchdogs;
@Mock
private ConnectivityModuleConnector mConnectivityModuleConnector;
@Mock
@@ -112,7 +114,8 @@ public class PackageWatchdogTest {
MockitoAnnotations.initMocks(this);
new File(InstrumentationRegistry.getContext().getFilesDir(),
"package-watchdog.xml").delete();
- adoptShellPermissions(Manifest.permission.READ_DEVICE_CONFIG);
+ adoptShellPermissions(Manifest.permission.READ_DEVICE_CONFIG,
+ Manifest.permission.WRITE_DEVICE_CONFIG);
mTestLooper = new TestLooper();
mSpyContext = spy(InstrumentationRegistry.getContext());
when(mSpyContext.getPackageManager()).thenReturn(mMockPackageManager);
@@ -157,12 +160,23 @@ public class PackageWatchdogTest {
return storedValue == null ? defaultValue : Long.parseLong(storedValue);
}
).when(() -> SystemProperties.getLong(anyString(), anyLong()));
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED,
+ Boolean.toString(true), false);
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
+ Integer.toString(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT), false);
+
+ mAllocatedWatchdogs = new ArrayList<>();
}
@After
public void tearDown() throws Exception {
dropShellPermissions();
mSession.finishMocking();
+ mAllocatedWatchdogs.clear();
}
@Test
@@ -611,10 +625,6 @@ public class PackageWatchdogTest {
*/
@Test
public void testExplicitHealthCheckStateChanges() throws Exception {
- adoptShellPermissions(
- Manifest.permission.WRITE_DEVICE_CONFIG,
- Manifest.permission.READ_DEVICE_CONFIG);
-
TestController controller = new TestController();
PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
@@ -807,9 +817,6 @@ public class PackageWatchdogTest {
/** Test default values are used when device property is invalid. */
@Test
public void testInvalidConfig_watchdogTriggerFailureCount() {
- adoptShellPermissions(
- Manifest.permission.WRITE_DEVICE_CONFIG,
- Manifest.permission.READ_DEVICE_CONFIG);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
Integer.toString(-1), /*makeDefault*/false);
@@ -835,9 +842,6 @@ public class PackageWatchdogTest {
/** Test default values are used when device property is invalid. */
@Test
public void testInvalidConfig_watchdogTriggerDurationMillis() {
- adoptShellPermissions(
- Manifest.permission.WRITE_DEVICE_CONFIG,
- Manifest.permission.READ_DEVICE_CONFIG);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
Integer.toString(2), /*makeDefault*/false);
@@ -850,7 +854,6 @@ public class PackageWatchdogTest {
watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE);
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
- mTestLooper.dispatchAll();
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS + 1);
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -862,7 +865,6 @@ public class PackageWatchdogTest {
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
- mTestLooper.dispatchAll();
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS - 1);
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
@@ -917,9 +919,6 @@ public class PackageWatchdogTest {
/** Test we are notified when enough failures are triggered within any window. */
@Test
public void testFailureTriggerWindow() {
- adoptShellPermissions(
- Manifest.permission.WRITE_DEVICE_CONFIG,
- Manifest.permission.READ_DEVICE_CONFIG);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
Integer.toString(3), /*makeDefault*/false);
@@ -933,11 +932,9 @@ public class PackageWatchdogTest {
// Raise 2 failures at t=0 and t=900 respectively
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
- mTestLooper.dispatchAll();
moveTimeForwardAndDispatch(900);
watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
- mTestLooper.dispatchAll();
// Raise 2 failures at t=1100
moveTimeForwardAndDispatch(200);
@@ -1303,15 +1300,15 @@ public class PackageWatchdogTest {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED,
Boolean.toString(enabled), /*makeDefault*/false);
- //give time for DeviceConfig to broadcast the property value change
- try {
- Thread.sleep(SHORT_DURATION);
- } catch (InterruptedException e) {
- fail("Thread.sleep unexpectedly failed!");
+ // Call updateConfigs() so device config changes take effect immediately
+ for (PackageWatchdog watchdog : mAllocatedWatchdogs) {
+ watchdog.updateConfigs();
}
}
private void moveTimeForwardAndDispatch(long milliSeconds) {
+ // Exhaust all due runnables now which shouldn't be executed after time-leap
+ mTestLooper.dispatchAll();
mTestClock.moveTimeForward(milliSeconds);
mTestLooper.moveTimeForward(milliSeconds);
mTestLooper.dispatchAll();
@@ -1354,6 +1351,7 @@ public class PackageWatchdogTest {
verify(mConnectivityModuleConnector).registerHealthListener(
mConnectivityModuleCallbackCaptor.capture());
}
+ mAllocatedWatchdogs.add(watchdog);
return watchdog;
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 0db2b2af7260..7b2a07fd80f8 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -1225,4 +1225,44 @@ public class RollbackTest {
InstallUtils.dropShellPermissionIdentity();
}
}
+
+ /**
+ * Tests an app can be rolled back to the previous signing key.
+ *
+ * <p>The rollback capability in the signing lineage allows an app to be updated to an APK
+ * signed with a previous signing key in the lineage; however this often defeats the purpose
+ * of key rotation as a compromised key could then be used to roll an app back to the previous
+ * key. To avoid requiring the rollback capability to support app rollbacks the PackageManager
+ * allows an app to be rolled back to the previous signing key if the rollback install reason
+ * is set.
+ */
+ @Test
+ public void testRollbackAfterKeyRotation() throws Exception {
+ try {
+ InstallUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
+ Manifest.permission.MANAGE_ROLLBACKS);
+
+ // Uninstall TestApp.A
+ Uninstall.packages(TestApp.A);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+
+ // Install v1 of the app with the original signing key (without rollbacks enabled).
+ Install.single(TestApp.AOriginal1).commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+
+ // Upgrade from v1 to v2 with the rotated signing key, with rollbacks enabled.
+ Install.single(TestApp.ARotated2).setEnableRollback().commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+
+ // Roll back the app.
+ RollbackInfo available = waitForAvailableRollback(TestApp.A);
+ RollbackUtils.rollback(available.getRollbackId());
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ } finally {
+ InstallUtils.dropShellPermissionIdentity();
+ }
+ }
}
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
index ef973acf763b..861d221238ff 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
@@ -182,6 +182,24 @@ public class UsbHandlerTest {
@SmallTest
@Test
+ public void setFunctionsNcmAndRndis() {
+ final long rndisPlusNcm = UsbManager.FUNCTION_RNDIS | UsbManager.FUNCTION_NCM;
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_NCM));
+ assertEquals(UsbManager.FUNCTION_NCM, mUsbHandler.getEnabledFunctions() & rndisPlusNcm);
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ rndisPlusNcm));
+ assertEquals(rndisPlusNcm, mUsbHandler.getEnabledFunctions() & rndisPlusNcm);
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_NCM));
+ assertEquals(UsbManager.FUNCTION_NCM, mUsbHandler.getEnabledFunctions() & rndisPlusNcm);
+ }
+
+ @SmallTest
+ @Test
public void enableAdb() {
sendBootCompleteMessages(mUsbHandler);
Message msg = mUsbHandler.obtainMessage(MSG_ENABLE_ADB);
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index f6a2846c9b3c..e630da04a512 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -53,6 +53,7 @@ android_test {
jarjar_rules: "jarjar-rules.txt",
static_libs: [
"androidx.test.rules",
+ "bouncycastle-repackaged-unbundled",
"FrameworksNetCommonTests",
"frameworks-base-testutils",
"frameworks-net-integration-testutils",
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java
index 076e41d33a8d..1abd39a32bdf 100644
--- a/tests/net/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java
@@ -30,7 +30,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.net.VpnProfile;
import com.android.net.module.util.ProxyUtils;
-import com.android.org.bouncycastle.x509.X509V1CertificateGenerator;
+import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator;
import org.junit.Before;
import org.junit.Test;
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 68e5ce07bab3..bf73134952ee 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1283,32 +1283,10 @@ public class ConnectivityServiceTest {
}
private void updateUidNetworkingBlocked() {
- // Changes the return value of the mock NetworkPolicyManager's isUidNetworkingBlocked method
- // based on the current UID rules and restrict background setting. Note that the test never
- // pretends to be a foreground app, so always declare no connectivity if background
- // networking is not allowed.
- switch (mUidRules) {
- case RULE_REJECT_ALL:
- when(mNetworkPolicyManager.isUidNetworkingBlocked(anyInt(), anyBoolean()))
- .thenReturn(true);
- break;
-
- case RULE_REJECT_METERED:
- when(mNetworkPolicyManager.isUidNetworkingBlocked(anyInt(), eq(true)))
- .thenReturn(true);
- when(mNetworkPolicyManager.isUidNetworkingBlocked(anyInt(), eq(false)))
- .thenReturn(mRestrictBackground);
- break;
-
- case RULE_ALLOW_METERED:
- case RULE_NONE:
- when(mNetworkPolicyManager.isUidNetworkingBlocked(anyInt(), anyBoolean()))
- .thenReturn(mRestrictBackground);
- break;
-
- default:
- fail("Unknown policy rule " + mUidRules);
- }
+ doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked(
+ i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */,
+ mRestrictBackground)
+ ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
}
private void setUidRulesChanged(int uidRules) throws RemoteException {
@@ -6917,7 +6895,7 @@ public class ConnectivityServiceTest {
cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
mCellNetworkAgent);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
- assertEquals(null, mCm.getActiveNetwork());
+ assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
@@ -6930,17 +6908,21 @@ public class ConnectivityServiceTest {
setUidRulesChanged(RULE_NONE);
cellNetworkCallback.assertNoCallback();
- // Restrict the network based on BackgroundRestricted.
+ // Restrict background data. Networking is not blocked because the network is unmetered.
setRestrictBackgroundChanged(true);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
- assertEquals(null, mCm.getActiveNetwork());
+ assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
-
setRestrictBackgroundChanged(true);
cellNetworkCallback.assertNoCallback();
- setRestrictBackgroundChanged(false);
+
+ setUidRulesChanged(RULE_ALLOW_METERED);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+
+ setRestrictBackgroundChanged(false);
cellNetworkCallback.assertNoCallback();
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index ef2653204af5..c64f4bc605f1 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -20,6 +20,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.AlarmManager;
@@ -948,8 +949,9 @@ public class WifiNl80211Manager {
* has been set up).
*/
public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
- @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs,
- @Nullable Bundle extraScanningParams) {
+ @SuppressLint("NullableCollection") @Nullable Set<Integer> freqs,
+ @SuppressLint("NullableCollection") @Nullable List<byte[]> hiddenNetworkSSIDs,
+ @SuppressLint("NullableCollection") @Nullable Bundle extraScanningParams) {
IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
if (scannerImpl == null) {
Log.e(TAG, "No valid wificond scanner interface handler for iface=" + ifaceName);