summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java13
-rw-r--r--apex/media/framework/Android.bp1
-rw-r--r--apex/permission/framework/Android.bp5
-rw-r--r--apex/sdkextensions/framework/Android.bp1
-rw-r--r--apex/statsd/aidl/android/os/IStatsCompanionService.aidl3
-rw-r--r--apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java60
-rw-r--r--api/current.txt2
-rw-r--r--api/test-current.txt3
-rw-r--r--cmds/statsd/benchmark/filter_value_benchmark.cpp21
-rw-r--r--cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp21
-rw-r--r--cmds/statsd/benchmark/metric_util.cpp62
-rw-r--r--cmds/statsd/benchmark/metric_util.h6
-rw-r--r--cmds/statsd/src/FieldValue.cpp18
-rw-r--r--cmds/statsd/src/FieldValue.h12
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp26
-rw-r--r--cmds/statsd/src/atoms.proto113
-rw-r--r--cmds/statsd/src/config/ConfigManager.cpp34
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp14
-rw-r--r--cmds/statsd/src/external/puller_util.cpp32
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp8
-rw-r--r--cmds/statsd/src/logd/LogEvent.h1
-rw-r--r--cmds/statsd/src/matchers/matcher_util.cpp18
-rw-r--r--cmds/statsd/tests/FieldValue_test.cpp32
-rw-r--r--cmds/statsd/tests/LogEntryMatcher_test.cpp79
-rw-r--r--cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp14
-rw-r--r--cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp7
-rw-r--r--cmds/statsd/tests/external/StatsCallbackPuller_test.cpp8
-rw-r--r--cmds/statsd/tests/external/StatsPuller_test.cpp8
-rw-r--r--cmds/statsd/tests/external/puller_util_test.cpp36
-rw-r--r--cmds/statsd/tests/log_event/LogEventQueue_test.cpp8
-rw-r--r--cmds/statsd/tests/metrics/CountMetricProducer_test.cpp13
-rw-r--r--cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp7
-rw-r--r--cmds/statsd/tests/metrics/EventMetricProducer_test.cpp7
-rw-r--r--cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp7
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp48
-rw-r--r--cmds/statsd/tests/shell/ShellSubscriber_test.cpp6
-rw-r--r--cmds/statsd/tests/state/StateTracker_test.cpp16
-rw-r--r--cmds/statsd/tests/statsd_test_util.cpp248
-rw-r--r--cmds/statsd/tests/statsd_test_util.h7
-rw-r--r--cmds/svc/src/com/android/commands/svc/PowerCommand.java7
-rw-r--r--core/java/android/app/AppOps.md29
-rw-r--r--core/java/android/app/AppOpsManager.java1
-rw-r--r--core/java/android/app/IWindowToken.aidl2
-rw-r--r--core/java/android/app/Notification.java4
-rw-r--r--core/java/android/app/SystemServiceRegistry.java23
-rw-r--r--core/java/android/app/WindowContext.java19
-rw-r--r--core/java/android/app/WindowTokenClient.java39
-rw-r--r--core/java/android/app/admin/DevicePolicyKeyguardService.java24
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java2
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java32
-rw-r--r--core/java/android/content/ContentResolver.java13
-rw-r--r--core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java5
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java24
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java3
-rw-r--r--core/java/android/os/PowerManager.java15
-rw-r--r--core/java/android/provider/Settings.java18
-rw-r--r--core/java/android/service/autofill/IInlineSuggestionRenderService.aidl2
-rw-r--r--core/java/android/service/autofill/InlineSuggestionRenderService.java13
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureService.java2
-rw-r--r--core/java/android/view/IWindowManager.aidl16
-rw-r--r--core/java/android/view/SurfaceControl.java3
-rw-r--r--core/java/android/view/ViewRootImpl.java17
-rw-r--r--core/java/android/view/WindowManagerGlobal.java8
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java2
-rw-r--r--core/java/android/view/textclassifier/ConversationActions.java8
-rw-r--r--core/java/android/window/DisplayAreaOrganizer.java4
-rw-r--r--core/java/android/window/VirtualDisplayTaskEmbedder.java2
-rw-r--r--core/java/com/android/internal/policy/DecorView.java21
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java21
-rw-r--r--core/java/com/android/internal/util/FunctionalUtils.java34
-rwxr-xr-xcore/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java34
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp4
-rw-r--r--core/proto/android/app/settings_enums.proto10
-rw-r--r--core/proto/android/stats/devicepolicy/device_policy_enums.proto11
-rw-r--r--core/res/AndroidManifest.xml17
-rw-r--r--core/tests/coretests/src/android/app/WindowContextTest.java74
-rw-r--r--core/tests/coretests/src/android/view/WindowMetricsTest.java8
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--data/etc/services.core.protolog.json12
-rw-r--r--location/java/android/location/ILocationManager.aidl4
-rw-r--r--location/java/android/location/LocationManager.java24
-rw-r--r--location/java/android/location/LocationRequest.java4
-rw-r--r--packages/CarSystemUI/res/layout/notification_center_activity.xml2
-rw-r--r--packages/CarSystemUI/res/layout/notification_panel_container.xml3
-rw-r--r--packages/CarSystemUI/res/layout/sysui_overlay_window.xml4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java2
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java544
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java8
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarTrustAgentUnlockDialogHelper.java268
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java131
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java563
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java3
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java431
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java8
-rw-r--r--packages/SettingsLib/res/values/strings.xml4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java5
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java11
-rw-r--r--packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml8
-rw-r--r--packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml11
-rw-r--r--packages/SystemUI/res/drawable/ic_device_cooking_off.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_device_cooking_on.xml34
-rw-r--r--packages/SystemUI/res/drawable/ic_device_display_off.xml25
-rw-r--r--packages/SystemUI/res/drawable/ic_device_display_on.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_device_door_off.xml31
-rw-r--r--packages/SystemUI/res/drawable/ic_device_door_on.xml31
-rw-r--r--packages/SystemUI/res/drawable/ic_device_garage_on.xml7
-rw-r--r--packages/SystemUI/res/drawable/ic_device_outdoor_garden_off.xml25
-rw-r--r--packages/SystemUI/res/drawable/ic_device_outdoor_garden_on.xml31
-rw-r--r--packages/SystemUI/res/drawable/ic_device_pergola_on.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_device_thermostat_off.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_device_thermostat_on.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_device_tv_off.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_device_tv_on.xml5
-rw-r--r--packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml9
-rw-r--r--packages/SystemUI/res/drawable/ic_device_unknown_off.xml36
-rw-r--r--packages/SystemUI/res/drawable/ic_device_unknown_on.xml35
-rw-r--r--packages/SystemUI/res/drawable/ic_device_water_off.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_device_water_on.xml25
-rw-r--r--packages/SystemUI/res/layout/controls_with_favorites.xml4
-rw-r--r--packages/SystemUI/res/layout/global_actions_grid_item_v2.xml7
-rw-r--r--packages/SystemUI/res/layout/global_actions_grid_v2.xml8
-rw-r--r--packages/SystemUI/res/layout/qs_media_panel.xml13
-rw-r--r--packages/SystemUI/res/values-sw320dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw360dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw392dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/colors.xml5
-rw-r--r--packages/SystemUI/res/values/dimens.xml5
-rw-r--r--packages/SystemUI/res/values/styles.xml3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java33
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java22
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java533
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java221
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java242
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt162
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java105
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt112
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java44
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java26
-rw-r--r--packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java93
-rw-r--r--packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java (renamed from packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java)99
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java3
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java3
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java3
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java11
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java33
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java70
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java9
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java10
-rw-r--r--services/core/java/com/android/server/am/AnrHelper.java2
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java3
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.md129
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java10
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java10
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricServiceBase.java4
-rw-r--r--services/core/java/com/android/server/content/ContentService.java3
-rw-r--r--services/core/java/com/android/server/display/color/AppSaturationController.java41
-rw-r--r--services/core/java/com/android/server/display/color/ColorDisplayService.java12
-rw-r--r--services/core/java/com/android/server/location/AppOpsHelper.java6
-rw-r--r--services/core/java/com/android/server/location/CallerIdentity.java38
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java117
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java38
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java4
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java31
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java52
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java13
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java5
-rw-r--r--services/core/java/com/android/server/pm/dex/DexManager.java89
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java4
-rw-r--r--services/core/java/com/android/server/textclassifier/IconsContentProvider.java124
-rw-r--r--services/core/java/com/android/server/textclassifier/IconsUriHelper.java144
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicy.java41
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java25
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java18
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java38
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java69
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java104
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java36
-rw-r--r--services/incremental/IncrementalService.cpp2
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp17
-rw-r--r--services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java34
-rw-r--r--services/tests/servicestests/res/raw/comp_policies_primary.xml5
-rw-r--r--services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml3
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java87
-rw-r--r--services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/textclassifier/IconsUriHelperTest.java134
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java63
-rw-r--r--services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java7
-rw-r--r--startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java17
-rw-r--r--telecomm/java/android/telecom/Conference.java30
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java8
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapter.java16
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapterServant.java24
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionService.java5
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java14
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl2
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java20
-rw-r--r--telephony/java/android/telephony/CbGeoUtils.java61
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java12
-rw-r--r--tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java59
-rw-r--r--tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java11
-rw-r--r--tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java2
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java40
-rw-r--r--tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java3
-rw-r--r--tools/stats_log_api_gen/atoms_info_writer.cpp23
-rw-r--r--wifi/jarjar-rules.txt3
267 files changed, 6172 insertions, 2882 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index b6f85b2d06bf..1ab79380fb0d 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1364,6 +1364,19 @@ public class AppStandbyController implements AppStandbyInternal {
if (DEBUG) {
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
}
+ } else if (newBucket == STANDBY_BUCKET_RARE
+ && getBucketForLocked(packageName, userId, elapsedRealtime)
+ == STANDBY_BUCKET_RESTRICTED) {
+ // Prediction doesn't think the app will be used anytime soon and
+ // it's been long enough that it could just time out into restricted,
+ // so time it out there instead. Using TIMEOUT will allow prediction
+ // to raise the bucket when it needs to.
+ newBucket = STANDBY_BUCKET_RESTRICTED;
+ reason = REASON_MAIN_TIMEOUT;
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Prediction to RARE overridden by timeout into RESTRICTED");
+ }
}
}
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 99e82e7a3367..579963b71cf7 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -94,7 +94,6 @@ stubs_defaults {
// TODO(b/135922046) remove this
include_dirs: ["frameworks/base/core/java"],
},
- sdk_version: "system_current",
}
droidstubs {
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 793247e88614..fc9052e8beac 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -46,12 +46,10 @@ stubs_defaults {
name: "framework-permission-stubs-defaults",
srcs: [ ":framework-permission-sources" ],
libs: [ "framework-annotations-lib" ],
- sdk_version: "system_current",
}
droidstubs {
name: "framework-permission-stubs-srcs-publicapi",
- sdk_version: "system_current",
defaults: [
"framework-module-stubs-defaults-publicapi",
"framework-permission-stubs-defaults",
@@ -60,7 +58,6 @@ droidstubs {
droidstubs {
name: "framework-permission-stubs-srcs-systemapi",
- sdk_version: "system_current",
defaults: [
"framework-module-stubs-defaults-systemapi",
"framework-permission-stubs-defaults",
@@ -69,7 +66,6 @@ droidstubs {
droidstubs {
name: "framework-permission-api-module_libs_api",
- sdk_version: "system_current",
defaults: [
"framework-module-api-defaults-module_libs_api",
"framework-permission-stubs-defaults",
@@ -78,7 +74,6 @@ droidstubs {
droidstubs {
name: "framework-permission-stubs-srcs-module_libs_api",
- sdk_version: "system_current",
defaults: [
"framework-module-stubs-defaults-module_libs_api",
"framework-permission-stubs-defaults",
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 707113b9672c..3eabb88fec7d 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -48,7 +48,6 @@ stubs_defaults {
name: "framework-sdkextensions-stubs-defaults",
srcs: [ ":framework-sdkextensions-sources" ],
libs: [ "framework-annotations-lib" ],
- sdk_version: "system_current",
}
droidstubs {
diff --git a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
index b94928f09ae0..5cdb3249501b 100644
--- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
@@ -59,9 +59,6 @@ interface IStatsCompanionService {
/** Cancel any alarm for the purpose of subscriber triggering. */
oneway void cancelAlarmForSubscriberTriggering();
- /** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
- oneway void triggerUidSnapshot();
-
/**
* Ask StatsCompanionService if the given permission is allowed for a particular process
* and user ID. statsd is incapable of doing this check itself because checkCallingPermission
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index aad51124c8d2..66e41cca96a7 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -153,7 +153,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
- private static void informAllUidsLocked(Context context) throws RemoteException {
+ private static void informAllUids(Context context) {
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
PackageManager pm = context.getPackageManager();
final List<UserHandle> users = um.getUserHandles(true);
@@ -168,18 +168,26 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
Log.e(TAG, "Failed to create a pipe to send uid map data.", e);
return;
}
- sStatsd.informAllUidData(fds[0]);
- try {
- fds[0].close();
- } catch (IOException e) {
- Log.e(TAG, "Failed to close the read side of the pipe.", e);
- }
- final ParcelFileDescriptor writeFd = fds[1];
HandlerThread backgroundThread = new HandlerThread(
"statsCompanionService.bg", THREAD_PRIORITY_BACKGROUND);
backgroundThread.start();
Handler handler = new Handler(backgroundThread.getLooper());
handler.post(() -> {
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd == null) {
+ return;
+ }
+ try {
+ statsd.informAllUidData(fds[0]);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send uid map to statsd");
+ }
+ try {
+ fds[0].close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close the read side of the pipe.", e);
+ }
+ final ParcelFileDescriptor writeFd = fds[1];
FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd);
try {
ProtoOutputStream output = new ProtoOutputStream(fout);
@@ -188,7 +196,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
for (UserHandle userHandle : users) {
List<PackageInfo> pi =
pm.getInstalledPackagesAsUser(PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_ANY_USER,
+ | PackageManager.MATCH_ANY_USER
+ | PackageManager.MATCH_APEX,
userHandle.getIdentifier());
for (int j = 0; j < pi.size(); j++) {
if (pi.get(j).applicationInfo != null) {
@@ -319,19 +328,9 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private static final class UserUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- Log.w(TAG, "Could not access statsd for UserUpdateReceiver");
- return;
- }
- try {
- // Pull the latest state of UID->app name, version mapping.
- // Needed since the new user basically has a version of every app.
- informAllUidsLocked(context);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to inform statsd latest update of all apps", e);
- }
- }
+ // Pull the latest state of UID->app name, version mapping.
+ // Needed since the new user basically has a version of every app.
+ informAllUids(context);
}
}
@@ -589,21 +588,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
@Override // Binder call
- public void triggerUidSnapshot() {
- StatsCompanion.enforceStatsdCallingUid();
- synchronized (sStatsdLock) {
- final long token = Binder.clearCallingIdentity();
- try {
- informAllUidsLocked(mContext);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to trigger uid snapshot.", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
- @Override // Binder call
public boolean checkPermission(String permission, int pid, int uid) {
StatsCompanion.enforceStatsdCallingUid();
return mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED;
@@ -707,7 +691,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
try {
// Pull the latest state of UID->app name, version mapping when
// statsd starts.
- informAllUidsLocked(mContext);
+ informAllUids(mContext);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/api/current.txt b/api/current.txt
index b4db1f7bc10a..80e2d00b3e48 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -46230,7 +46230,7 @@ package android.telecom {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
method public String getDefaultDialerPackage();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(String);
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1Number(android.telecom.PhoneAccountHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle);
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccountHandle getSimCallManager();
diff --git a/api/test-current.txt b/api/test-current.txt
index c29f1c4c657d..641767c7bccd 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -214,6 +214,7 @@ package android.app {
field public static final String OPSTR_GPS = "android:gps";
field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
field public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
+ field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage";
field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
field public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
field public static final String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
@@ -5215,10 +5216,10 @@ package android.window {
method public void onDisplayAreaAppeared(@NonNull android.window.WindowContainerToken);
method public void onDisplayAreaVanished(@NonNull android.window.WindowContainerToken);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void registerOrganizer(int);
+ field public static final int FEATURE_DEFAULT_TASK_CONTAINER = 1; // 0x1
field public static final int FEATURE_ROOT = 0; // 0x0
field public static final int FEATURE_SYSTEM_FIRST = 0; // 0x0
field public static final int FEATURE_SYSTEM_LAST = 10000; // 0x2710
- field public static final int FEATURE_TASK_CONTAINER = 1; // 0x1
field public static final int FEATURE_UNDEFINED = -1; // 0xffffffff
field public static final int FEATURE_VENDOR_FIRST = 10001; // 0x2711
field public static final int FEATURE_WINDOW_TOKENS = 2; // 0x2
diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp
index 28bf21ae52bf..743ccc4ed451 100644
--- a/cmds/statsd/benchmark/filter_value_benchmark.cpp
+++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
#include <vector>
-#include "benchmark/benchmark.h"
+
#include "FieldValue.h"
#include "HashableDimensionKey.h"
+#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
-#include "stats_log_util.h"
+#include "metric_util.h"
#include "stats_event.h"
+#include "stats_log_util.h"
namespace android {
namespace os {
@@ -34,24 +36,13 @@ static void createLogEventAndMatcher(LogEvent* event, FieldMatcher* field_matche
std::vector<int> attributionUids = {100, 100};
std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
+ writeAttribution(statsEvent, attributionUids, attributionTags);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
AStatsEvent_writeFloat(statsEvent, 3.2f);
AStatsEvent_writeString(statsEvent, "LOCATION");
AStatsEvent_writeInt64(statsEvent, 990);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- event->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, event);
field_matcher->set_field(1);
auto child = field_matcher->add_child();
diff --git a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
index c7d01cc406fc..7a455650a31b 100644
--- a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
+++ b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
#include <vector>
-#include "benchmark/benchmark.h"
+
#include "FieldValue.h"
#include "HashableDimensionKey.h"
+#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
-#include "stats_log_util.h"
+#include "metric_util.h"
#include "stats_event.h"
+#include "stats_log_util.h"
namespace android {
namespace os {
@@ -34,24 +36,13 @@ static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) {
std::vector<int> attributionUids = {100, 100};
std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
+ writeAttribution(statsEvent, attributionUids, attributionTags);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
AStatsEvent_writeFloat(statsEvent, 3.2f);
AStatsEvent_writeString(statsEvent, "LOCATION");
AStatsEvent_writeInt64(statsEvent, 990);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- event->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, event);
link->conditionId = 1;
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index 482d66fc7556..89fd3d9b29ab 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -247,21 +247,37 @@ FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields)
return dimensions;
}
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags) {
+ vector<const char*> cTags(attributionTags.size());
+ for (int i = 0; i < cTags.size(); i++) {
+ cTags[i] = attributionTags[i].c_str();
+ }
+
+ AStatsEvent_writeAttributionChain(statsEvent,
+ reinterpret_cast<const uint32_t*>(attributionUids.data()),
+ cTags.data(), attributionUids.size());
+}
+
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) {
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+
+ AStatsEvent_release(statsEvent);
+}
+
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
uint64_t timestampNs, const android::view::DisplayStateEnum state) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -272,24 +288,12 @@ std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, jobName.c_str());
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -319,24 +323,12 @@ std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs,
AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, name.c_str());
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/benchmark/metric_util.h b/cmds/statsd/benchmark/metric_util.h
index c5fcf7c27440..3efaa850a921 100644
--- a/cmds/statsd/benchmark/metric_util.h
+++ b/cmds/statsd/benchmark/metric_util.h
@@ -18,6 +18,7 @@
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "src/StatsLogProcessor.h"
#include "src/logd/LogEvent.h"
+#include "stats_event.h"
#include "statslog.h"
namespace android {
@@ -92,6 +93,11 @@ FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
FieldMatcher CreateAttributionUidDimensions(const int atomId,
const std::vector<Position>& positions);
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags);
+
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent);
+
// Create log event for screen state changed.
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
uint64_t timestampNs, const android::view::DisplayStateEnum state);
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 4385964f7f0e..cfc1de49e259 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -120,9 +120,9 @@ bool isAttributionUidField(const FieldValue& value) {
}
int32_t getUidIfExists(const FieldValue& value) {
- // the field is uid field if the field is the uid field in attribution node or marked as
- // is_uid in atoms.proto
- bool isUid = isAttributionUidField(value) || isUidField(value.mField, value.mValue);
+ // the field is uid field if the field is the uid field in attribution node
+ // or annotated as such in the atom
+ bool isUid = isAttributionUidField(value) || isUidField(value);
return isUid ? value.mValue.int_value : -1;
}
@@ -134,16 +134,8 @@ bool isAttributionUidField(const Field& field, const Value& value) {
return false;
}
-bool isUidField(const Field& field, const Value& value) {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(field.getTag());
-
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto
- return field.getDepth() == 0 && field.getPosAtDepth(0) == uidField &&
- value.getType() == INT;
- }
-
- return false;
+bool isUidField(const FieldValue& fieldValue) {
+ return fieldValue.mAnnotations.isUidField();
}
Value::Value(const Value& from) {
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 3536e5a5c962..92e09ea0f8f9 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -367,7 +367,8 @@ public:
enum {
NESTED_POS = 0x0,
PRIMARY_POS = 0x1,
- EXCLUSIVE_POS = 0x2
+ EXCLUSIVE_POS = 0x2,
+ UID_POS = 0x3
};
inline void setNested(bool nested) { setBitmaskAtPos(NESTED_POS, nested); }
@@ -376,6 +377,8 @@ public:
inline void setExclusiveState(bool exclusive) { setBitmaskAtPos(EXCLUSIVE_POS, exclusive); }
+ inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); }
+
inline void setResetState(int resetState) { mResetState = resetState; }
// Default value = false
@@ -387,6 +390,9 @@ public:
// Default value = false
inline bool isExclusiveState() const { return getValueFromBitmask(EXCLUSIVE_POS); }
+ // Default value = false
+ inline bool isUidField() const { return getValueFromBitmask(UID_POS); }
+
// If a reset state is not sent in the StatsEvent, returns -1. Note that a
// reset satate is only sent if and only if a reset should be triggered.
inline int getResetState() const { return mResetState; }
@@ -402,7 +408,7 @@ private:
}
// This is a bitmask over all annotations stored in boolean form. Because
- // there are only 3 booleans, just one byte is required.
+ // there are only 4 booleans, just one byte is required.
uint8_t mBooleanBitmask = 0;
int mResetState = -1;
@@ -449,7 +455,7 @@ int getUidIfExists(const FieldValue& value);
void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
bool isAttributionUidField(const Field& field, const Value& value);
-bool isUidField(const Field& field, const Value& value);
+bool isUidField(const FieldValue& fieldValue);
bool equalDimensions(const std::vector<Matcher>& dimension_a,
const std::vector<Matcher>& dimension_b);
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 982a63e3e08c..325cbc7e80e5 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -138,13 +138,6 @@ void StatsLogProcessor::onPeriodicAlarmFired(
}
}
-void updateUid(Value* value, int hostUid) {
- int uid = value->int_value;
- if (uid != hostUid) {
- value->setInt(hostUid);
- }
-}
-
void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(event->GetTagId()) !=
android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
@@ -154,22 +147,15 @@ void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event
}
if (isAttributionUidField(value)) {
const int hostUid = mUidMap->getHostUidOrSelf(value.mValue.int_value);
- updateUid(&value.mValue, hostUid);
+ value.mValue.setInt(hostUid);
}
}
} else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(event->GetTagId());
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto,
- // starting from 1
- if (uidField > 0 && (int)event->getValues().size() >= uidField &&
- (event->getValues())[uidField - 1].mValue.getType() == INT) {
- Value& value = (*event->getMutableValues())[uidField - 1].mValue;
- const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
- updateUid(&value, hostUid);
- } else {
- ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
- }
+ int uidFieldIndex = event->getUidFieldIndex();
+ if (uidFieldIndex != -1) {
+ Value& value = (*event->getMutableValues())[uidFieldIndex].mValue;
+ const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
+ value.setInt(hostUid);
}
}
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index bd15264c008f..38b421f70477 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -418,7 +418,9 @@ message Atom {
AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"];
SharesheetStarted sharesheet_started = 259 [(module) = "framework"];
RankingSelected ranking_selected = 260 [(module) = "framework"];
- TvSettingsUIInteracted tvsettings_ui_interacted = 261;
+ TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"];
+ LauncherStaticLayout launcher_snapshot = 262 [(module) = "sysui"];
+ PackageInstallerV2Reported package_installer_v2_reported = 263 [(module) = "framework"];
SdkExtensionStatus sdk_extension_status = 354;
}
@@ -2979,14 +2981,98 @@ message ExclusionRectStateChanged {
optional int32 duration_millis = 7;
}
+/**
+ * Logs when Launcher (HomeScreen) UI has changed or was interacted.
+ *
+ * Logged from:
+ * packages/apps/Launcher3
+ */
message LauncherUIChanged {
- optional android.stats.launcher.LauncherAction action = 1;
+ optional android.stats.launcher.LauncherAction action = 1 [deprecated = true];
optional android.stats.launcher.LauncherState src_state = 2;
optional android.stats.launcher.LauncherState dst_state = 3;
- optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES];
- optional bool is_swipe_up_enabled = 5;
+ optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES, deprecated = true];
+ optional bool is_swipe_up_enabled = 5 [deprecated = true];
+
+ // The event id (e.g., app launch, drag and drop, long press)
+ optional int32 event_id = 6;
+ // The event's source or target id (e.g., icon, task, button)
+ optional int32 target_id = 7;
+ // If the target needs to be tracked, use this id field
+ optional int32 instance_id = 8;
+ optional int32 uid = 9 [(is_uid) = true];
+ optional string package_name = 10;
+ optional string component_name = 11;
+
+ // (x, y) coordinate and the index information of the target on the container
+ optional int32 grid_x = 12;
+ optional int32 grid_y = 13;
+ optional int32 page_id = 14;
+
+ // e.g., folder icon's (x, y) location and index information on the workspace
+ optional int32 grid_x_parent = 15;
+ optional int32 grid_y_parent = 16;
+ optional int32 page_id_parent = 17;
+
+ // e.g., SEARCHBOX_ALLAPPS, FOLDER_WORKSPACE
+ optional int32 hierarchy = 18;
+
+ optional bool is_work_profile = 19;
+
+ // Used to store the predicted rank of the target
+ optional int32 rank = 20;
+
+ // e.g., folderLabelState can be captured in the following two fields
+ optional int32 from_state = 21;
+ optional int32 to_state = 22;
+
+ // e.g., autofilled or suggested texts that are not user entered
+ optional string edittext = 23;
}
+/**
+ * Used for snapshot of the HomeScreen UI elements
+ *
+ * Logged from:
+ * packages/apps/Launcher3
+ */
+message LauncherStaticLayout {
+ // The event id (e.g., snapshot, drag and drop)
+ optional int32 event_id = 1;
+ // The event's source or target id (e.g., icon, shortcut, widget)
+ optional int32 target_id = 2;
+ // If the target needs to be tracked, use this id field
+ optional int32 instance_id = 3;
+ optional int32 uid = 4 [(is_uid) = true];
+ optional string package_name = 5;
+ optional string component_name = 6;
+
+ // (x, y) coordinate and the index information of the target on the container
+ optional int32 grid_x = 7;
+ optional int32 grid_y = 8;
+ optional int32 page_id = 9;
+
+ // e.g., folder icon's (x, y) location and index information on the workspace
+ optional int32 grid_x_parent = 10;
+ optional int32 grid_y_parent = 11;
+ optional int32 page_id_parent = 12;
+
+ // e.g., WORKSPACE, HOTSEAT, FOLDER_WORKSPACE, FOLDER_HOTSEAT
+ optional int32 hierarchy = 13;
+
+ optional bool is_work_profile = 14;
+
+ // e.g., PIN, WIDGET TRAY, APPS TRAY, PREDICTION
+ optional int32 origin = 15;
+}
+
+/**
+ * Logs when Wallpaper or ThemePicker UI has changed.
+ *
+ * Logged from:
+ * packages/apps/ThemePicker
+ * packages/apps/WallpaperPicker2
+ */
message StyleUIChanged {
optional android.stats.style.Action action = 1;
optional int32 color_package_hash = 2;
@@ -9223,6 +9309,25 @@ message TvSettingsUIInteracted {
}
/**
+ * Logs information about a package installation using package installer V2 APIs.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
+ */
+message PackageInstallerV2Reported {
+ // Whether this installation uses Incremental File System
+ optional bool is_incremental = 1;
+ // Name of the package that is intended to be installed
+ optional string package_name = 2;
+ // The duration between when the install was requested to when the install has completed
+ optional int64 duration_millis = 3;
+ // Installation result in final integer, which are SystemApi's.
+ // Return_code 1 indicates success.
+ // For full list, see frameworks/base/core/java/android/content/pm/PackageManager.java
+ optional int32 return_code = 4;
+}
+
+/**
* Logs settings provider values.
*
* Use DeviceConfig.getProperties to get a list Setting key, query the data from content provider,
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 6d9c644bb40e..bbae3fef7934 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -43,20 +43,23 @@ using android::base::StringPrintf;
using std::unique_ptr;
struct ConfigReceiverDeathCookie {
- ConfigReceiverDeathCookie(sp<ConfigManager> configManager, const ConfigKey& configKey,
- const shared_ptr<IPendingIntentRef>& pir):
- mConfigManager(configManager),
- mConfigKey(configKey),
- mPir(pir) {}
+ ConfigReceiverDeathCookie(const wp<ConfigManager>& configManager, const ConfigKey& configKey,
+ const shared_ptr<IPendingIntentRef>& pir) :
+ mConfigManager(configManager), mConfigKey(configKey), mPir(pir) {
+ }
- sp<ConfigManager> mConfigManager;
+ wp<ConfigManager> mConfigManager;
ConfigKey mConfigKey;
shared_ptr<IPendingIntentRef> mPir;
};
void ConfigManager::configReceiverDied(void* cookie) {
auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie);
- sp<ConfigManager>& thiz = cookie_->mConfigManager;
+ sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
+ if (!thiz) {
+ return;
+ }
+
ConfigKey& configKey = cookie_->mConfigKey;
shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
@@ -74,20 +77,23 @@ void ConfigManager::configReceiverDied(void* cookie) {
}
struct ActiveConfigChangedReceiverDeathCookie {
- ActiveConfigChangedReceiverDeathCookie(sp<ConfigManager> configManager, const int uid,
- const shared_ptr<IPendingIntentRef>& pir):
- mConfigManager(configManager),
- mUid(uid),
- mPir(pir) {}
+ ActiveConfigChangedReceiverDeathCookie(const wp<ConfigManager>& configManager, const int uid,
+ const shared_ptr<IPendingIntentRef>& pir) :
+ mConfigManager(configManager), mUid(uid), mPir(pir) {
+ }
- sp<ConfigManager> mConfigManager;
+ wp<ConfigManager> mConfigManager;
int mUid;
shared_ptr<IPendingIntentRef> mPir;
};
void ConfigManager::activeConfigChangedReceiverDied(void* cookie) {
auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie);
- sp<ConfigManager>& thiz = cookie_->mConfigManager;
+ sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
+ if (!thiz) {
+ return;
+ }
+
int uid = cookie_->mUid;
shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 79a7e8d318e2..ebe961014336 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -44,19 +44,23 @@ namespace statsd {
// Stores the puller as a wp to avoid holding a reference in case it is unregistered and
// pullAtomCallbackDied is never called.
struct PullAtomCallbackDeathCookie {
- PullAtomCallbackDeathCookie(sp<StatsPullerManager> pullerManager, const PullerKey& pullerKey,
- const wp<StatsPuller>& puller)
- : mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) {
+ PullAtomCallbackDeathCookie(const wp<StatsPullerManager>& pullerManager,
+ const PullerKey& pullerKey, const wp<StatsPuller>& puller) :
+ mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) {
}
- sp<StatsPullerManager> mPullerManager;
+ wp<StatsPullerManager> mPullerManager;
PullerKey mPullerKey;
wp<StatsPuller> mPuller;
};
void StatsPullerManager::pullAtomCallbackDied(void* cookie) {
PullAtomCallbackDeathCookie* cookie_ = static_cast<PullAtomCallbackDeathCookie*>(cookie);
- sp<StatsPullerManager>& thiz = cookie_->mPullerManager;
+ sp<StatsPullerManager> thiz = cookie_->mPullerManager.promote();
+ if (!thiz) {
+ return;
+ }
+
const PullerKey& pullerKey = cookie_->mPullerKey;
wp<StatsPuller> puller = cookie_->mPuller;
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index aee725698c30..90247cf9d68c 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -49,10 +49,14 @@ using namespace std;
*/
void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
int tagId, const vector<int>& additiveFieldsVec) {
- if ((android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) ==
- android::util::AtomsInfo::kAtomsWithAttributionChain.end()) &&
- (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
- android::util::AtomsInfo::kAtomsWithUidField.end())) {
+ bool hasAttributionChain = (android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) !=
+ android::util::AtomsInfo::kAtomsWithAttributionChain.end());
+ // To check if any LogEvent has a uid field, we can just check the first
+ // LogEvent because all atoms with this tagId should have the uid
+ // annotation.
+ bool hasUidField = (data[0]->getUidFieldIndex() != -1);
+
+ if (!hasAttributionChain && !hasUidField) {
VLOG("No uid or attribution chain to merge, atom %d", tagId);
return;
}
@@ -75,19 +79,13 @@ void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const
}
}
} else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto,
- // starting from 1
- if (uidField > 0 && (int)event->getValues().size() >= uidField &&
- (event->getValues())[uidField - 1].mValue.getType() == INT) {
- Value& value = (*event->getMutableValues())[uidField - 1].mValue;
- const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
- value.setInt(hostUid);
- } else {
- ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
- return;
- }
+ int uidFieldIndex = event->getUidFieldIndex();
+ if (uidFieldIndex != -1) {
+ Value& value = (*event->getMutableValues())[uidFieldIndex].mValue;
+ const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
+ value.setInt(hostUid);
+ } else {
+ ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
}
}
}
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 96bf04f4f6d6..8b6a86464155 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -240,14 +240,20 @@ void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
last[1] = last[2] = false;
}
+// Assumes that mValues is not empty
+bool LogEvent::checkPreviousValueType(Type expected) {
+ return mValues[mValues.size() - 1].mValue.getType() == expected;
+}
+
void LogEvent::parseIsUidAnnotation(uint8_t annotationType) {
- if (mValues.empty() || annotationType != BOOL_TYPE) {
+ if (mValues.empty() || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
bool isUid = readNextValue<uint8_t>();
if (isUid) mUidFieldIndex = mValues.size() - 1;
+ mValues[mValues.size() - 1].mAnnotations.setUidField(isUid);
}
void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 9e21c777e6ff..4eeb7d64a463 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -213,6 +213,7 @@ private:
void parseExclusiveStateAnnotation(uint8_t annotationType);
void parseTriggerStateResetAnnotation(uint8_t annotationType);
void parseStateNestedAnnotation(uint8_t annotationType);
+ bool checkPreviousValueType(Type expected);
/**
* The below three variables are only valid during the execution of
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 1f8bbd7f528c..2b4c6a3cbf1e 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -81,18 +81,17 @@ bool combinationMatch(const vector<int>& children, const LogicalOperation& opera
return matched;
}
-bool tryMatchString(const UidMap& uidMap, const Field& field, const Value& value,
- const string& str_match) {
- if (isAttributionUidField(field, value) || isUidField(field, value)) {
- int uid = value.int_value;
+bool tryMatchString(const UidMap& uidMap, const FieldValue& fieldValue, const string& str_match) {
+ if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
+ int uid = fieldValue.mValue.int_value;
auto aidIt = UidMap::sAidToUidMapping.find(str_match);
if (aidIt != UidMap::sAidToUidMapping.end()) {
return ((int)aidIt->second) == uid;
}
std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/);
return packageNames.find(str_match) != packageNames.end();
- } else if (value.getType() == STRING) {
- return value.str_value == str_match;
+ } else if (fieldValue.mValue.getType() == STRING) {
+ return fieldValue.mValue.str_value == str_match;
}
return false;
}
@@ -228,8 +227,7 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
}
case FieldValueMatcher::ValueMatcherCase::kEqString: {
for (int i = start; i < end; i++) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue,
- matcher.eq_string())) {
+ if (tryMatchString(uidMap, values[i], matcher.eq_string())) {
return true;
}
}
@@ -240,7 +238,7 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
for (int i = start; i < end; i++) {
bool notEqAll = true;
for (const auto& str : str_list.str_value()) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) {
+ if (tryMatchString(uidMap, values[i], str)) {
notEqAll = false;
break;
}
@@ -255,7 +253,7 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
const auto& str_list = matcher.eq_any_string();
for (int i = start; i < end; i++) {
for (const auto& str : str_list.str_value()) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) {
+ if (tryMatchString(uidMap, values[i], str)) {
return true;
}
}
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index 0bf24f1d3606..f5ba8fd0d60d 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -41,22 +41,10 @@ void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timest
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, name.c_str());
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
@@ -66,22 +54,10 @@ void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timest
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // anonymous namespace
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 6f4c8e48eff2..26423d464027 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -18,6 +18,7 @@
#include <log/logprint.h>
#include <stdio.h>
+#include "annotations.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/matcher_util.h"
#include "stats_event.h"
@@ -48,15 +49,9 @@ void makeIntLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t tim
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
@@ -64,15 +59,9 @@ void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t t
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
AStatsEvent_writeFloat(statsEvent, floatValue);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
@@ -80,32 +69,20 @@ void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
AStatsEvent_writeString(statsEvent, name.c_str());
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
-void makeIntStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
- const int32_t value, const string& name) {
+void makeIntWithBoolAnnotationLogEvent(LogEvent* logEvent, const int32_t atomId,
+ const int32_t field, const uint8_t annotationId,
+ const bool annotationValue) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
- AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_writeString(statsEvent, name.c_str());
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
+ AStatsEvent_writeInt32(statsEvent, field);
+ AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
@@ -115,22 +92,10 @@ void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, name.c_str());
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
@@ -141,13 +106,8 @@ void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t ti
AStatsEvent_writeBool(statsEvent, bool1);
AStatsEvent_writeBool(statsEvent, bool2);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // anonymous namespace
@@ -416,21 +376,20 @@ TEST(AtomMatcherTest, TestUidFieldMatcher) {
simpleMatcher->add_field_value_matcher()->set_field(1);
simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0");
- // Set up the event
+ // Make event without is_uid annotation.
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeIntLogEvent(&event1, TAG_ID, 0, 1111);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+ // Make event with is_uid annotation.
LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeIntStringLogEvent(&event2, TAG_ID_2, 0, 1111, "some value");
-
- // Tag not in kAtomsWithUidField
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+ makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID_2, 1111, ANNOTATION_ID_IS_UID, true);
- // Tag found in kAtomsWithUidField and has matching uid
+ // Event has is_uid annotation, so mapping from uid to package name occurs.
simpleMatcher->set_atom_id(TAG_ID_2);
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
- // Tag found in kAtomsWithUidField but has non-matching uid
+ // Event has is_uid annotation, but uid maps to different package name.
simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
}
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 7febb35355a3..ba5b032b80d0 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -67,22 +67,12 @@ void makeWakeLockEvent(LogEvent* logEvent, uint32_t atomId, uint64_t timestamp,
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
vector<std::string> tags(uids.size()); // vector of empty strings
- vector<const char*> cTags(uids.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = tags[i].c_str();
- }
- AStatsEvent_writeAttributionChain(statsEvent, reinterpret_cast<const uint32_t*>(uids.data()),
- cTags.data(), uids.size());
+ writeAttribution(statsEvent, uids, tags);
AStatsEvent_writeString(statsEvent, wl.c_str());
AStatsEvent_writeInt32(statsEvent, acquire);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // anonymous namespace
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 81e1c05c1cf4..60403f2a3e0f 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -84,14 +84,9 @@ std::unique_ptr<LogEvent> CreateAppStartOccurredEvent(
AStatsEvent_writeString(statsEvent, calling_pkg_name.c_str());
AStatsEvent_writeInt32(statsEvent, is_instant_app);
AStatsEvent_writeInt32(statsEvent, activity_start_msec);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index 6aff9ef80a71..4b9bac127dc8 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -190,13 +190,13 @@ TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) {
int32_t uid = 123;
values.push_back(value);
- StatsPullerManager pullerManager;
- pullerManager.RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs,
- vector<int32_t>(), cb);
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ pullerManager->RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs,
+ vector<int32_t>(), cb);
vector<shared_ptr<LogEvent>> dataHolder;
int64_t startTimeNs = getElapsedRealtimeNs();
// Returns false, since StatsPuller code will evaluate the timeout.
- EXPECT_FALSE(pullerManager.Pull(pullTagId, {uid}, &dataHolder));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId, {uid}, &dataHolder));
int64_t endTimeNs = getElapsedRealtimeNs();
int64_t actualPullDurationNs = endTimeNs - startTimeNs;
diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp
index e8200d5c7f52..504335845cab 100644
--- a/cmds/statsd/tests/external/StatsPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsPuller_test.cpp
@@ -64,16 +64,10 @@ std::unique_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value)
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, pullTagId);
AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
AStatsEvent_writeInt64(statsEvent, value);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
index 15425d8810dc..c2cfb371d329 100644
--- a/cmds/statsd/tests/external/puller_util_test.cpp
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -21,8 +21,10 @@
#include <vector>
#include "../metrics/metrics_test_helper.h"
+#include "annotations.h"
#include "stats_event.h"
#include "statslog_statsdtest.h"
+#include "tests/statsd_test_util.h"
#ifdef __ANDROID__
@@ -52,15 +54,15 @@ int hostNonAdditiveData = 22;
void extractIntoVector(vector<shared_ptr<LogEvent>> events,
vector<vector<int>>& ret) {
- ret.clear();
- status_t err;
- for (const auto& event : events) {
- vector<int> vec;
- vec.push_back(event->GetInt(1, &err));
- vec.push_back(event->GetInt(2, &err));
- vec.push_back(event->GetInt(3, &err));
- ret.push_back(vec);
- }
+ ret.clear();
+ status_t err;
+ for (const auto& event : events) {
+ vector<int> vec;
+ vec.push_back(event->GetInt(1, &err));
+ vec.push_back(event->GetInt(2, &err));
+ vec.push_back(event->GetInt(3, &err));
+ ret.push_back(vec);
+ }
}
std::shared_ptr<LogEvent> makeUidLogEvent(uint64_t timestampNs, int uid, int data1, int data2) {
@@ -69,16 +71,12 @@ std::shared_ptr<LogEvent> makeUidLogEvent(uint64_t timestampNs, int uid, int dat
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
AStatsEvent_writeInt32(statsEvent, data1);
AStatsEvent_writeInt32(statsEvent, data2);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -86,16 +84,10 @@ std::shared_ptr<LogEvent> makeNonUidAtomLogEvent(uint64_t timestampNs, int data1
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, nonUidAtomTagId);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, data1);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
index 6dc041f9fb6e..a15f95bef358 100644
--- a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
+++ b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
@@ -21,6 +21,7 @@
#include <thread>
#include "stats_event.h"
+#include "tests/statsd_test_util.h"
namespace android {
namespace os {
@@ -37,14 +38,9 @@ std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, 10);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index d55996cb1b7a..65f8de69711d 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -46,26 +46,17 @@ void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeString(statsEvent, uid.c_str());
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // namespace
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 6143dc0dc5d1..30f815962160 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -26,6 +26,7 @@
#include "src/condition/ConditionWizard.h"
#include "src/stats_log_util.h"
#include "stats_event.h"
+#include "tests/statsd_test_util.h"
using namespace android::os::statsd;
using namespace testing;
@@ -48,12 +49,8 @@ void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // namespace
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index e58bbb7893d7..97647a7e0867 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -43,14 +43,9 @@ void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, strin
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeString(statsEvent, str.c_str());
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // anonymous namespace
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 2fe05a4430c3..42d0d5d8c530 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -64,14 +64,9 @@ shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t v
AStatsEvent_writeInt32(statsEvent, value1);
AStatsEvent_writeString(statsEvent, str1.c_str());
AStatsEvent_writeInt32(statsEvent, value2);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
-
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
} // anonymous namespace
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index b623a0978f18..009e49a5523f 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -611,7 +611,7 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 110));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
@@ -656,7 +656,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
@@ -665,14 +665,14 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 10);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
// Next value should create a new bucket.
LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10);
+ CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 10);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs);
@@ -812,10 +812,10 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
@@ -856,7 +856,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
valueProducer.mCondition = ConditionState::kFalse;
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has 1 slice
EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
@@ -864,7 +864,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
valueProducer.onConditionChangedLocked(true, bucketStartTimeNs + 15);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
@@ -875,7 +875,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
EXPECT_EQ(20, curInterval.value.long_value);
LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 1, 30);
+ CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 30);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
// has one slice
@@ -886,7 +886,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
LogEvent event4(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 1, 40);
+ CreateRepeatedValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 40);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
// has one slice
@@ -1195,10 +1195,10 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
@@ -1238,10 +1238,10 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
@@ -1283,10 +1283,10 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 15);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
@@ -1331,10 +1331,10 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 15);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
@@ -1374,10 +1374,10 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 1, 15);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
@@ -1398,7 +1398,7 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
// no change in data.
LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 1, 15);
+ CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -1408,7 +1408,7 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
EXPECT_EQ(true, curInterval.hasValue);
LogEvent event4(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 1, 15);
+ CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -2166,7 +2166,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed)
// Bucket start.
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 1, 1, 110));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
@@ -2174,7 +2174,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed)
// Bucket end.
allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 140));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index ac3ad690f81e..7b952d7a392e 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -171,13 +171,9 @@ shared_ptr<LogEvent> makeCpuActiveTimeAtom(int32_t uid, int64_t timeMillis) {
AStatsEvent_overwriteTimestamp(statsEvent, 1111L);
AStatsEvent_writeInt32(statsEvent, uid);
AStatsEvent_writeInt64(statsEvent, timeMillis);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index a5b8e1c50c33..78c80bc8307c 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -62,7 +62,7 @@ int getStateInt(StateManager& mgr, int atomId, const HashableDimensionKey& query
// START: build event functions.
// Incorrect event - missing fields
-std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName,
+std::unique_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName,
int state) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
@@ -72,14 +72,9 @@ std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string&
AStatsEvent_writeString(statsEvent, packageName.c_str());
// Missing field 3 - using_alert_window.
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -93,14 +88,9 @@ std::unique_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::stri
AStatsEvent_writeString(statsEvent, packageName.c_str());
AStatsEvent_writeInt32(statsEvent, true); // using_alert_window
AStatsEvent_writeString(statsEvent, "string"); // exclusive state: string instead of int
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
// END: build event functions.
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 7d765d3fbbf5..ed3cf5b96b42 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -507,23 +507,26 @@ void getPartialWakelockKey(int uid, HashableDimensionKey* key) {
}
// END: get primary key functions
-shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags) {
+ vector<const char*> cTags(attributionTags.size());
+ for (int i = 0; i < cTags.size(); i++) {
+ cTags[i] = attributionTags[i].c_str();
+ }
- AStatsEvent_writeInt32(statsEvent, value1);
- AStatsEvent_writeInt32(statsEvent, value2);
+ AStatsEvent_writeAttributionChain(statsEvent,
+ reinterpret_cast<const uint32_t*>(attributionUids.data()),
+ cTags.data(), attributionUids.size());
+}
+
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) {
AStatsEvent_build(statsEvent);
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
- return logEvent;
+ AStatsEvent_release(statsEvent);
}
void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
@@ -534,31 +537,14 @@ void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs,
AStatsEvent_writeInt32(statsEvent, value1);
AStatsEvent_writeInt32(statsEvent, value2);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
-shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2, int32_t value3) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
- AStatsEvent_writeInt32(statsEvent, value1);
- AStatsEvent_writeInt32(statsEvent, value2);
- AStatsEvent_writeInt32(statsEvent, value3);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2) {
shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
-
+ CreateTwoValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2);
return logEvent;
}
@@ -571,29 +557,14 @@ void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeN
AStatsEvent_writeInt32(statsEvent, value1);
AStatsEvent_writeInt32(statsEvent, value2);
AStatsEvent_writeInt32(statsEvent, value3);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
-shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
- AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3) {
shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
-
+ CreateThreeValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2, value3);
return logEvent;
}
@@ -605,26 +576,13 @@ void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTi
AStatsEvent_writeInt32(statsEvent, value);
AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
-shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) {
shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
-
+ CreateRepeatedValueLogEvent(logEvent.get(), atomId, eventTimeNs, value);
return logEvent;
}
@@ -632,12 +590,14 @@ void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs)
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) {
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ CreateNoValuesLogEvent(logEvent.get(), atomId, eventTimeNs);
+ return logEvent;
}
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
@@ -645,16 +605,10 @@ std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -662,16 +616,10 @@ std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -679,16 +627,10 @@ std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -696,16 +638,10 @@ std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestam
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::PLUGGED_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -713,16 +649,10 @@ std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampN
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::SCREEN_BRIGHTNESS_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, level);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -733,24 +663,12 @@ std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, jobName.c_str());
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -780,25 +698,13 @@ std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs,
AStatsEvent_setAtomId(statsEvent, util::WAKELOCK_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
AStatsEvent_writeString(statsEvent, wakelockName.c_str());
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -828,14 +734,9 @@ std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
AStatsEvent_writeString(statsEvent, "pkg_name");
AStatsEvent_writeString(statsEvent, "class_name");
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -858,24 +759,12 @@ std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs,
AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, name.c_str());
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -904,14 +793,9 @@ std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
AStatsEvent_writeInt32(statsEvent, uid);
AStatsEvent_writeString(statsEvent, "");
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -928,14 +812,9 @@ std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, cons
AStatsEvent_writeInt32(statsEvent, uid);
AStatsEvent_writeString(statsEvent, "eventType");
AStatsEvent_writeString(statsEvent, "processName");
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -948,14 +827,9 @@ std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(uint64_t timestampNs, in
AStatsEvent_writeInt32(statsEvent, hostUid);
AStatsEvent_writeInt32(statsEvent, isolatedUid);
AStatsEvent_writeInt32(statsEvent, is_create);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -967,14 +841,9 @@ std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
AStatsEvent_writeInt32(statsEvent, uid);
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -988,26 +857,14 @@ std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs,
AStatsEvent_setAtomId(statsEvent, util::BLE_SCAN_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeInt32(statsEvent, state);
AStatsEvent_writeInt32(statsEvent, filtered); // filtered
AStatsEvent_writeInt32(statsEvent, firstMatch); // first match
AStatsEvent_writeInt32(statsEvent, opportunistic); // opportunistic
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -1023,14 +880,9 @@ std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, co
AStatsEvent_writeString(statsEvent, packageName.c_str());
AStatsEvent_writeInt32(statsEvent, usingAlertWindow);
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index f24705a0c89f..d6ea77eb2c7d 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -25,6 +25,7 @@
#include "src/hash.h"
#include "src/logd/LogEvent.h"
#include "src/stats_log_util.h"
+#include "stats_event.h"
#include "statslog_statsdtest.h"
namespace android {
@@ -189,6 +190,12 @@ void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey
void getPartialWakelockKey(int uid, HashableDimensionKey* key);
// END: get primary key functions
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags);
+
+// Builds statsEvent to get buffer that is parsed into logEvent then releases statsEvent.
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent);
+
shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
int32_t value2);
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index 3180b77f5700..957ebfbef799 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -24,6 +24,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.sysprop.InitProperties;
public class PowerCommand extends Svc.Command {
private static final int FORCE_SUSPEND_DELAY_DEFAULT_MILLIS = 0;
@@ -103,6 +104,8 @@ public class PowerCommand extends Svc.Command {
pm.reboot(false, mode, true);
} catch (RemoteException e) {
maybeLogRemoteException("Failed to reboot.");
+ } catch (Exception e) {
+ System.err.println("Failed to reboot: " + e.getMessage());
}
return;
} else if ("shutdown".equals(args[1])) {
@@ -138,7 +141,9 @@ public class PowerCommand extends Svc.Command {
// if it is already in shutdown flow.
private void maybeLogRemoteException(String msg) {
String powerProp = SystemProperties.get("sys.powerctl");
- if (powerProp.isEmpty()) {
+ // Also check if userspace reboot is ongoing, since in case of userspace reboot value of the
+ // sys.powerctl property will be reset.
+ if (powerProp.isEmpty() && !InitProperties.userspace_reboot_in_progress().orElse(false)) {
System.err.println(msg);
}
}
diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md
index bee701addca8..ad1a30146663 100644
--- a/core/java/android/app/AppOps.md
+++ b/core/java/android/app/AppOps.md
@@ -116,14 +116,27 @@ for app-ops. It also delays the changes by a _settle time_. This delay is needed
can fluctuate when switching apps. By delaying the change the appops service is not affected by
those.
-The proc state is used for two use cases: Firstly, Tracking remembers the proc state for each
-tracked event. Secondly, `noteOp`/`checkOp` calls for app-op that are set to `MODE_FOREGROUND` are
-translated using the `AppOpsService.UidState.evalMode` method into `MODE_ALLOWED` when the app is
-counted as foreground and `MODE_IGNORED` when the app is counted as background. `checkOpRaw`
-calls are not affected.
-
-The current proc state for an app can be read from `dumpsys appops`. The tracking information can
-be read from `dumpsys appops`
+In addition to proc state, the `AppOpsService` also receives process capability update from the
+`ActivityManagerService`. Proc capability specifies what while-in-use(`MODE_FOREGROUND`) operations
+ the proc is allowed to perform in its current proc state. There are three proc capabilities
+ defined so far:
+`PROCESS_CAPABILITY_FOREGROUND_LOCATION`, `PROCESS_CAPABILITY_FOREGROUND_CAMERA` and
+`PROCESS_CAPABILITY_FOREGROUND_MICROPHONE`, they correspond to the while-in-use operation of
+location, camera and microphone (microphone is `RECORD_AUDIO`).
+
+In `ActivityManagerService`, `PROCESS_STATE_TOP` and `PROCESS_STATE_PERSISTENT` have all
+three capabilities, `PROCESS_STATE_FOREGROUND_SERVICE` has capabilities defined by
+ `foregroundServiceType` that is specified in foreground service's manifest file. A client process
+ can pass its capabilities to service using `BIND_INCLUDE_CAPABILITIES` flag.
+
+The proc state and capability are used for two use cases: Firstly, Tracking remembers the proc state
+ for each tracked event. Secondly, `noteOp`/`checkOp` calls for app-op that are set to
+ `MODE_FOREGROUND` are translated using the `AppOpsService.UidState.evalMode` method into
+ `MODE_ALLOWED` when the app has the capability and `MODE_IGNORED` when the app does not have the
+ capability. `checkOpRaw` calls are not affected.
+
+The current proc state and capability for an app can be read from `dumpsys appops`.
+The tracking information can be read from `dumpsys appops`
```
Uid u0a118:
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 46b06fb64b80..3a708a6f699b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1395,6 +1395,7 @@ public class AppOpsManager {
public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages";
/** @hide Access all external storage */
@SystemApi
+ @TestApi
public static final String OPSTR_MANAGE_EXTERNAL_STORAGE =
"android:manage_external_storage";
diff --git a/core/java/android/app/IWindowToken.aidl b/core/java/android/app/IWindowToken.aidl
index 8ea881fba09c..3627b0f13a7f 100644
--- a/core/java/android/app/IWindowToken.aidl
+++ b/core/java/android/app/IWindowToken.aidl
@@ -30,4 +30,6 @@ import android.view.IWindow;
*/
oneway interface IWindowToken {
void onConfigurationChanged(in Configuration newConfig, int newDisplayId);
+
+ void onWindowTokenRemoved();
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 7a593498e967..babeffc120bb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7589,10 +7589,8 @@ public class Notification implements Parcelable
>= Build.VERSION_CODES.P;
boolean isOneToOne;
CharSequence nameReplacement = null;
- Icon avatarReplacement = null;
if (!atLeastP) {
isOneToOne = TextUtils.isEmpty(conversationTitle);
- avatarReplacement = mBuilder.mN.mLargeIcon;
if (hasOnlyWhiteSpaceSenders()) {
isOneToOne = true;
nameReplacement = conversationTitle;
@@ -7641,7 +7639,7 @@ public class Notification implements Parcelable
contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed",
isCollapsed);
contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement",
- avatarReplacement);
+ mBuilder.mN.mLargeIcon);
contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
nameReplacement);
contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsOneToOne",
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 91a857225324..e599a5ce81ef 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1658,6 +1658,9 @@ public final class SystemServiceRegistry {
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;
final int[] gates = ctx.mServiceInitializationStateArray;
+ boolean interrupted = false;
+
+ T ret = null;
for (;;) {
boolean doInitialize = false;
@@ -1665,7 +1668,8 @@ public final class SystemServiceRegistry {
// Return it if we already have a cached instance.
T service = (T) cache[mCacheIndex];
if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
- return service;
+ ret = service;
+ break; // exit the for (;;)
}
// If we get here, there's no cached instance.
@@ -1708,24 +1712,33 @@ public final class SystemServiceRegistry {
cache.notifyAll();
}
}
- return service;
+ ret = service;
+ break; // exit the for (;;)
}
// The other threads will wait for the first thread to call notifyAll(),
// and go back to the top and retry.
synchronized (cache) {
+ // Repeat until the state becomes STATE_READY or STATE_NOT_FOUND.
+ // We can't respond to interrupts here; just like we can't in the "doInitialize"
+ // path, so we remember the interrupt state here and re-interrupt later.
while (gates[mCacheIndex] < ContextImpl.STATE_READY) {
try {
+ // Clear the interrupt state.
+ interrupted |= Thread.interrupted();
cache.wait();
} catch (InterruptedException e) {
// This shouldn't normally happen, but if someone interrupts the
// thread, it will.
- Slog.wtf(TAG, "getService() interrupted");
- Thread.currentThread().interrupt();
- return null;
+ Slog.w(TAG, "getService() interrupted");
+ interrupted = true;
}
}
}
}
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ return ret;
}
public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
index 878993ebcd19..3a06c9d79fee 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -28,6 +28,8 @@ import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerImpl;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.ref.Reference;
/**
@@ -75,8 +77,6 @@ public class WindowContext extends ContextWrapper {
// config back to the client.
result = mWms.addWindowTokenWithOptions(
mToken, type, getDisplayId(), options, getPackageName());
-
- // TODO(window-context): remove token with a DeathObserver
} catch (RemoteException e) {
mOwnsToken = false;
throw e.rethrowFromSystemServer();
@@ -100,6 +100,13 @@ public class WindowContext extends ContextWrapper {
@Override
protected void finalize() throws Throwable {
+ release();
+ super.finalize();
+ }
+
+ /** Used for test to invoke because we can't invoke finalize directly. */
+ @VisibleForTesting
+ public void release() {
if (mOwnsToken) {
try {
mWms.removeWindowToken(mToken, getDisplayId());
@@ -108,6 +115,12 @@ public class WindowContext extends ContextWrapper {
throw e.rethrowFromSystemServer();
}
}
- super.finalize();
+ destroy();
+ }
+
+ void destroy() {
+ final ContextImpl impl = (ContextImpl) getBaseContext();
+ impl.scheduleFinalCleanup(getClass().getName(), "WindowContext");
+ Reference.reachabilityFence(this);
}
}
diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java
index ed0179bb9839..301960ec53f9 100644
--- a/core/java/android/app/WindowTokenClient.java
+++ b/core/java/android/app/WindowTokenClient.java
@@ -20,6 +20,9 @@ import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
+import android.view.WindowManagerGlobal;
+
+import java.lang.ref.WeakReference;
/**
* Client implementation of {@link IWindowToken}. It can receive configuration change callbacks from
@@ -31,9 +34,9 @@ import android.os.IBinder;
public class WindowTokenClient extends IWindowToken.Stub {
/**
* Attached {@link Context} for this window token to update configuration and resources.
- * Initialized by {@link #attachContext(Context)}.
+ * Initialized by {@link #attachContext(WindowContext)}.
*/
- private Context mContext = null;
+ private WeakReference<WindowContext> mContextRef = null;
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
@@ -47,30 +50,46 @@ public class WindowTokenClient extends IWindowToken.Stub {
* @param context context to be attached
* @throws IllegalStateException if attached context has already existed.
*/
- void attachContext(@NonNull Context context) {
- if (mContext != null) {
+ void attachContext(@NonNull WindowContext context) {
+ if (mContextRef != null) {
throw new IllegalStateException("Context is already attached.");
}
- mContext = context;
- ContextImpl impl = ContextImpl.getImpl(mContext);
+ mContextRef = new WeakReference<>(context);
+ final ContextImpl impl = ContextImpl.getImpl(context);
impl.setResources(impl.createWindowContextResources());
}
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
- final int currentDisplayId = mContext.getDisplayId();
+ final Context context = mContextRef.get();
+ if (context == null) {
+ return;
+ }
+ final int currentDisplayId = context.getDisplayId();
final boolean displayChanged = newDisplayId != currentDisplayId;
- final Configuration config = new Configuration(mContext.getResources()
+ final Configuration config = new Configuration(context.getResources()
.getConfiguration());
final boolean configChanged = config.isOtherSeqNewer(newConfig)
&& config.updateFrom(newConfig) != 0;
if (displayChanged || configChanged) {
// TODO(ag/9789103): update resource manager logic to track non-activity tokens
- mResourcesManager.updateResourcesForActivity(asBinder(), config, newDisplayId,
+ mResourcesManager.updateResourcesForActivity(this, config, newDisplayId,
displayChanged);
}
if (displayChanged) {
- mContext.updateDisplay(newDisplayId);
+ context.updateDisplay(newDisplayId);
+ }
+ }
+
+ @Override
+ public void onWindowTokenRemoved() {
+ final WindowContext context = mContextRef.get();
+ if (context != null) {
+ context.destroy();
+ mContextRef.clear();
}
+ // If a secondary display is detached, release all views attached to this token.
+ WindowManagerGlobal.getInstance().closeAll(this, mContextRef.getClass().getName(),
+ "WindowContext");
}
}
diff --git a/core/java/android/app/admin/DevicePolicyKeyguardService.java b/core/java/android/app/admin/DevicePolicyKeyguardService.java
index db833ec478bd..473725f40cf1 100644
--- a/core/java/android/app/admin/DevicePolicyKeyguardService.java
+++ b/core/java/android/app/admin/DevicePolicyKeyguardService.java
@@ -16,12 +16,15 @@
package android.app.admin;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.view.SurfaceControlViewHost;
@@ -41,27 +44,34 @@ import android.view.SurfaceControlViewHost;
@SystemApi
public class DevicePolicyKeyguardService extends Service {
private static final String TAG = "DevicePolicyKeyguardService";
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
private IKeyguardCallback mCallback;
private final IKeyguardClient mClient = new IKeyguardClient.Stub() {
+ @MainThread
@Override
public void onCreateKeyguardSurface(@Nullable IBinder hostInputToken,
- IKeyguardCallback callback) {
+ @NonNull IKeyguardCallback callback) {
mCallback = callback;
- SurfaceControlViewHost.SurfacePackage surfacePackage =
- DevicePolicyKeyguardService.this.onCreateKeyguardSurface(hostInputToken);
+ mHandler.post(() -> {
+ SurfaceControlViewHost.SurfacePackage surfacePackage =
+ DevicePolicyKeyguardService.this.onCreateKeyguardSurface(hostInputToken);
- if (mCallback != null) {
try {
mCallback.onRemoteContentReady(surfacePackage);
} catch (RemoteException e) {
Log.e(TAG, "Failed to return created SurfacePackage", e);
}
- }
+ });
}
};
@Override
+ public void onDestroy() {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
+ @Override
@Nullable
public final IBinder onBind(@Nullable Intent intent) {
return mClient.asBinder();
@@ -97,6 +107,10 @@ public class DevicePolicyKeyguardService extends Service {
*/
@Nullable
public void dismiss() {
+ if (mCallback == null) {
+ Log.w(TAG, "KeyguardCallback was unexpectedly null");
+ return;
+ }
try {
mCallback.onDismiss();
} catch (RemoteException e) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index faf9ec61ffde..fb9adb730314 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8606,7 +8606,7 @@ public class DevicePolicyManager {
* <p>
* This method may be called on the {@code DevicePolicyManager} instance returned from
* {@link #getParentProfileInstance(ComponentName)}. Note that only a profile owner on
- * an organization-deviced can affect account types on the parent profile instance.
+ * an organization-owned device can affect account types on the parent profile instance.
*
* @return a list of account types for which account management has been disabled.
*
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index fc48e7f18f5f..f216db6fc717 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -979,14 +979,17 @@ public final class BluetoothAdapter {
8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) {
@Override
protected Integer recompute(Void query) {
- // This function must be called while holding the
- // mServiceLock, and with mService not null. The public
- // getState() method makes this guarantee.
try {
- return mService.getState();
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.getState();
+ }
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
+ return BluetoothAdapter.STATE_OFF;
}
};
@@ -1013,24 +1016,7 @@ public final class BluetoothAdapter {
@RequiresPermission(Manifest.permission.BLUETOOTH)
@AdapterState
public int getState() {
- int state = BluetoothAdapter.STATE_OFF;
-
- try {
- mServiceLock.readLock().lock();
- // The test for mService must either be outside the cache, or
- // the cache must be invalidated when mService changes.
- if (mService != null) {
- state = mBluetoothGetStateCache.query(null);
- }
- } catch (RuntimeException e) {
- if (e.getCause() instanceof RemoteException) {
- Log.e(TAG, "", e.getCause());
- } else {
- throw e;
- }
- } finally {
- mServiceLock.readLock().unlock();
- }
+ int state = mBluetoothGetStateCache.query(null);
// Consider all internal states as OFF
if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index e446f4fa5eb4..0a4627da223a 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -687,6 +687,19 @@ public abstract class ContentResolver implements ContentInterface {
public static final int NOTIFY_DELETE = 1 << 4;
/**
+ * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: typically set
+ * by a {@link ContentProvider} to indicate that this notification should
+ * not be subject to any delays when dispatching to apps running in the
+ * background.
+ * <p>
+ * Using this flag may negatively impact system health and performance, and
+ * should be used sparingly.
+ *
+ * @hide
+ */
+ public static final int NOTIFY_NO_DELAY = 1 << 15;
+
+ /**
* No exception, throttled by app standby normally.
* @hide
*/
diff --git a/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java b/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java
index 6f01796c93c2..5d4b48dac46b 100644
--- a/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java
+++ b/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java
@@ -22,4 +22,9 @@ public class SQLiteCantOpenDatabaseException extends SQLiteException {
public SQLiteCantOpenDatabaseException(String error) {
super(error);
}
+
+ /** @hide */
+ public SQLiteCantOpenDatabaseException(String error, Throwable cause) {
+ super(error, cause);
+ }
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index bcb3934a5b08..f7c96a3a02c1 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -36,6 +36,9 @@ import dalvik.system.CloseGuard;
import java.io.File;
import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
@@ -215,12 +218,31 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
}
private void open() {
+ final String file = mConfiguration.path;
final int cookie = mRecentOperations.beginOperation("open", null, null);
try {
- mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags,
+ mConnectionPtr = nativeOpen(file, mConfiguration.openFlags,
mConfiguration.label,
NoPreloadHolder.DEBUG_SQL_STATEMENTS, NoPreloadHolder.DEBUG_SQL_TIME,
mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
+ } catch (SQLiteCantOpenDatabaseException e) {
+ String message = String.format("Cannot open database '%s'", file);
+
+ final Path path = FileSystems.getDefault().getPath(file);
+ final Path dir = path.getParent();
+
+ if (!Files.isDirectory(dir)) {
+ message += ": Directory " + dir + " doesn't exist";
+ } else if (!Files.exists(path)) {
+ message += ": File " + path + " doesn't exist";
+ } else if (!Files.isReadable(path)) {
+ message += ": File " + path + " is not readable";
+ } else if (Files.isDirectory(path)) {
+ message += ": Path " + path + " is a directory";
+ } else {
+ message += ": Unknown reason";
+ }
+ throw new SQLiteCantOpenDatabaseException(message, e);
} finally {
mRecentOperations.endOperation(cookie);
}
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 91dae66d08ae..aa75f6042db8 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -17,6 +17,7 @@
package android.hardware.camera2;
import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
@@ -100,6 +101,8 @@ public abstract class CameraMetadata<TKey> {
*
* @hide
*/
+ @UnsupportedAppUsage(publicAlternatives = "This method is exposed for native "
+ + "{@code ACameraMetadata_fromCameraMetadata} in {@code libcamera2ndk}.")
public long getNativeMetadataPtr() {
if (mNativeInstance == null) {
return 0;
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index b8e1aa88c3a3..be2de0edda2d 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1483,12 +1483,25 @@ public final class PowerManager {
return mInteractiveCache.query(null);
}
+
+ /**
+ * Returns {@code true} if this device supports rebooting userspace.
+ *
+ * <p>This method exists solely for the sake of re-using same logic between {@code PowerManager}
+ * and {@code PowerManagerService}.
+ *
+ * @hide
+ */
+ public static boolean isRebootingUserspaceSupportedImpl() {
+ return InitProperties.is_userspace_reboot_supported().orElse(false);
+ }
+
/**
* Returns {@code true} if this device supports rebooting userspace.
*/
// TODO(b/138605180): add link to documentation once it's ready.
public boolean isRebootingUserspaceSupported() {
- return InitProperties.is_userspace_reboot_supported().orElse(false);
+ return isRebootingUserspaceSupportedImpl();
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 920302c75011..541883346e23 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1870,6 +1870,24 @@ public final class Settings {
public static final String EXTRA_APP_UID = "app_uid";
/**
+ * Activity Action: Show power menu settings.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_POWER_MENU_SETTINGS =
+ "android.settings.ACTION_POWER_MENU_SETTINGS";
+
+ /**
+ * Activity Action: Show controls settings.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_DEVICE_CONTROLS_SETTINGS =
+ "android.settings.ACTION_DEVICE_CONTROLS_SETTINGS";
+
+ /**
* Activity Action: Show a dialog with disabled by policy message.
* <p> If an user action is disabled by policy, this dialog can be triggered to let
* the user know about this.
diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
index dd434b440af4..bf0bb9e2a41f 100644
--- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
@@ -17,6 +17,7 @@
package android.service.autofill;
import android.os.IBinder;
+import android.os.RemoteCallback;
import android.service.autofill.IInlineSuggestionUiCallback;
import android.service.autofill.InlinePresentation;
@@ -29,4 +30,5 @@ oneway interface IInlineSuggestionRenderService {
void renderSuggestion(in IInlineSuggestionUiCallback callback,
in InlinePresentation presentation, int width, int height,
in IBinder hostInputToken, int displayId);
+ void getInlineSuggestionsRendererInfo(in RemoteCallback callback);
}
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index cba6608db1b8..e3ed21ff556d 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -30,6 +30,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
@@ -128,6 +129,11 @@ public abstract class InlineSuggestionRenderService extends Service {
}
}
+ private void handleGetInlineSuggestionsRendererInfo(@NonNull RemoteCallback callback) {
+ final Bundle rendererInfo = onGetInlineSuggestionsRendererInfo();
+ callback.sendResult(rendererInfo);
+ }
+
private void sendResult(@NonNull IInlineSuggestionUiCallback callback,
@Nullable SurfaceControlViewHost.SurfacePackage surface) {
try {
@@ -151,6 +157,13 @@ public abstract class InlineSuggestionRenderService extends Service {
InlineSuggestionRenderService.this, callback, presentation,
width, height, hostInputToken, displayId));
}
+
+ @Override
+ public void getInlineSuggestionsRendererInfo(@NonNull RemoteCallback callback) {
+ mHandler.sendMessage(obtainMessage(
+ InlineSuggestionRenderService::handleGetInlineSuggestionsRendererInfo,
+ InlineSuggestionRenderService.this, callback));
+ }
}.asBinder();
}
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index ef55f0615ec7..46cb65babc1c 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -747,7 +747,7 @@ public abstract class ContentCaptureService extends Service {
void initializeForDelegate(DataShareReadAdapterDelegate delegate,
DataShareReadAdapter adapter, Executor executor) {
mDataShareReadAdapterHardReferences.put(delegate, adapter);
- mExecutorHardReferences.remove(delegate, executor);
+ mExecutorHardReferences.put(delegate, executor);
}
Executor getExecutor(DataShareReadAdapterDelegate delegate) {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 84ac90bae258..b0bacb955f80 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -124,13 +124,20 @@ interface IWindowManager
* @param type Window type to be used with this token.
* @param options A bundle used to pass window-related options.
* @param displayId The ID of the display where this token should be added.
- * @param packageName The name of package to request to add window token.
+ * @param packageName The name of package to request to add window token. Could be {@code null}
+ * if callers holds the MANAGE_APP_TOKENS permission.
* @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code
* otherwise.
*/
int addWindowTokenWithOptions(IBinder token, int type, int displayId, in Bundle options,
String packageName);
void addWindowToken(IBinder token, int type, int displayId);
+ /**
+ * Remove window token on a specific display.
+ *
+ * @param token Token to be removed
+ * @displayId The ID of the display where this token should be removed.
+ */
void removeWindowToken(IBinder token, int displayId);
void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
@@ -735,4 +742,11 @@ interface IWindowManager
* Called to show global actions.
*/
void showGlobalActions();
+
+ /**
+ * Sets layer tracing flags for SurfaceFlingerTrace.
+ *
+ * @param flags see definition in SurfaceTracing.cpp
+ */
+ void setLayerTracingFlags(int flags);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ab65e3a5c849..1086774fc8ff 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2888,7 +2888,8 @@ public final class SurfaceControl implements Parcelable {
/**
* Acquire a frame rate flexibility token, which allows surface flinger to freely switch display
* frame rates. This is used by CTS tests to put the device in a consistent state. See
- * ISurfaceComposer::acquireFrameRateFlexibilityToken().
+ * ISurfaceComposer::acquireFrameRateFlexibilityToken(). The caller must have the
+ * ACCESS_SURFACE_FLINGER permission, or else the call will fail, returning 0.
* @hide
*/
@TestApi
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 750b1eddd222..dab11088b130 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3017,6 +3017,10 @@ public final class ViewRootImpl implements ViewParent,
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw();
}
+ if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC) != 0) {
+ reportNextDraw();
+ setUseBLASTSyncTransaction();
+ }
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
@@ -3725,7 +3729,7 @@ public final class ViewRootImpl implements ViewParent,
if (needFrameCompleteCallback) {
final Handler handler = mAttachInfo.mHandler;
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
- finishBLASTSync();
+ finishBLASTSync(!reportNextDraw);
handler.postAtFrontOfQueue(() -> {
if (reportNextDraw) {
// TODO: Use the frame number
@@ -3759,7 +3763,7 @@ public final class ViewRootImpl implements ViewParent,
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false;
- finishBLASTSync();
+ finishBLASTSync(true /* apply */);
}
} finally {
mIsDrawing = false;
@@ -9576,10 +9580,15 @@ public final class ViewRootImpl implements ViewParent,
mNextDrawUseBLASTSyncTransaction = true;
}
- private void finishBLASTSync() {
+ private void finishBLASTSync(boolean apply) {
if (mNextReportConsumeBLAST) {
mNextReportConsumeBLAST = false;
- mRtBLASTSyncTransaction.apply();
+
+ if (apply) {
+ mRtBLASTSyncTransaction.apply();
+ } else {
+ mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction);
+ }
}
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 410d9afe73da..ab968d798745 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -102,6 +102,14 @@ public final class WindowManagerGlobal {
public static final int RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS = 0x40;
/**
+ * This flag indicates the client should not directly submit it's next frame,
+ * but instead should pass it in the postDrawTransaction of
+ * {@link WindowManagerService#finishDrawing}. This is used by the WM
+ * BLASTSyncEngine to synchronize rendering of multiple windows.
+ */
+ public static final int RELAYOUT_RES_BLAST_SYNC = 0x80;
+
+ /**
* Flag for relayout: the client will be later giving
* internal insets; as a result, the window will not impact other window
* layouts until the insets are given.
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index edc6b1251f2c..4519aefe65fe 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -832,7 +832,7 @@ public final class ContentCaptureManager {
void initializeForDelegate(DataShareAdapterDelegate delegate, DataShareWriteAdapter adapter,
Executor executor) {
mWriteAdapterHardReferences.put(delegate, adapter);
- mExecutorHardReferences.remove(delegate, executor);
+ mExecutorHardReferences.put(delegate, executor);
}
Executor getExecutor(DataShareAdapterDelegate delegate) {
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 842ba2975b3b..6ad5cb913553 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -27,8 +27,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.SpannedString;
-import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
@@ -491,7 +489,11 @@ public final class ConversationActions implements Parcelable {
*/
@NonNull
public Builder setMaxSuggestions(@IntRange(from = -1) int maxSuggestions) {
- mMaxSuggestions = Preconditions.checkArgumentNonnegative(maxSuggestions);
+ if (maxSuggestions < -1) {
+ throw new IllegalArgumentException("maxSuggestions has to be greater than or "
+ + "equal to -1.");
+ }
+ mMaxSuggestions = maxSuggestions;
return this;
}
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index eee222b9bf4c..6ae70b779960 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -33,8 +33,8 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
public static final int FEATURE_SYSTEM_FIRST = 0;
// The Root display area on a display
public static final int FEATURE_ROOT = FEATURE_SYSTEM_FIRST;
- // Display area hosting the task container.
- public static final int FEATURE_TASK_CONTAINER = FEATURE_SYSTEM_FIRST + 1;
+ // Display area hosting the default task container.
+ public static final int FEATURE_DEFAULT_TASK_CONTAINER = FEATURE_SYSTEM_FIRST + 1;
// Display area hosting non-activity window tokens.
public static final int FEATURE_WINDOW_TOKENS = FEATURE_SYSTEM_FIRST + 2;
diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java
index 1afbfeb96fc3..0f26d5d00f6d 100644
--- a/core/java/android/window/VirtualDisplayTaskEmbedder.java
+++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java
@@ -16,7 +16,6 @@
package android.window;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
@@ -247,7 +246,6 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
protected ActivityOptions prepareActivityOptions(ActivityOptions options) {
options = super.prepareActivityOptions(options);
options.setLaunchDisplayId(getDisplayId());
- options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
return options;
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 71cf5cad316e..b6c58e16b51c 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -68,7 +68,6 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.LayerDrawable;
-import android.os.Build.VERSION_CODES;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
@@ -120,7 +119,6 @@ import com.android.internal.widget.DecorCaptionView;
import com.android.internal.widget.FloatingToolbar;
import java.util.List;
-import java.util.function.Function;
/** @hide */
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
@@ -283,11 +281,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private Insets mLastBackgroundInsets = Insets.NONE;
private boolean mDrawLegacyNavigationBarBackground;
- /**
- * Whether the app targets an SDK that uses the new insets APIs.
- */
- private boolean mUseNewInsetsApi;
-
private PendingInsetsController mPendingInsetsController = new PendingInsetsController();
DecorView(Context context, int featureId, PhoneWindow window,
@@ -319,7 +312,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
initResizingPaints();
mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK);
- mUseNewInsetsApi = context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.R;
}
void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
@@ -1189,23 +1181,23 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
// these flags wouldn't make the window draw behind the navigation bar, unless
// LAYOUT_HIDE_NAVIGATION was set.
//
- // Note: Once the app targets R+, we no longer do this logic because we can't rely on
- // SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION to indicate whether the app wants to handle it by
- // themselves.
+ // Note: Once the app uses the R+ Window.setDecorFitsSystemWindows(false) API we no longer
+ // consume insets because they might no longer set SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION.
boolean hideNavigation = (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
|| !(controller == null || controller.isRequestedVisible(ITYPE_NAVIGATION_BAR));
+ boolean decorFitsSystemWindows = mWindow.mDecorFitsSystemWindows;
boolean forceConsumingNavBar = (mForceWindowDrawsBarBackgrounds
&& (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
+ && decorFitsSystemWindows
&& !hideNavigation)
|| (mLastShouldAlwaysConsumeSystemBars && hideNavigation);
boolean consumingNavBar =
((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
- && !hideNavigation
- // TODO IME wrap_content windows need to have margin to work properly
- && (!mUseNewInsetsApi || isImeWindow))
+ && decorFitsSystemWindows
+ && !hideNavigation)
|| forceConsumingNavBar;
// If we didn't request fullscreen layout, but we still got it because of the
@@ -1216,6 +1208,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
|| (attrs.flags & FLAG_FULLSCREEN) != 0
|| !(controller == null || controller.isRequestedVisible(ITYPE_STATUS_BAR));
boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
+ && decorFitsSystemWindows
&& (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
&& (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
&& mForceWindowDrawsBarBackgrounds
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 138d0dd39537..25c114f4b7c1 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -343,8 +343,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
/** @see ViewRootImpl#mActivityConfigCallback */
private ActivityConfigCallback mActivityConfigCallback;
- private OnContentApplyWindowInsetsListener mPendingOnContentApplyWindowInsetsListener =
- sDefaultContentInsetsApplier;
+ boolean mDecorFitsSystemWindows = true;
static class WindowManagerHolder {
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
@@ -2138,9 +2137,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
/** Notify when decor view is attached to window and {@link ViewRootImpl} is available. */
void onViewRootImplSet(ViewRootImpl viewRoot) {
viewRoot.setActivityConfigCallback(mActivityConfigCallback);
- viewRoot.setOnContentApplyWindowInsetsListener(
- mPendingOnContentApplyWindowInsetsListener);
- mPendingOnContentApplyWindowInsetsListener = null;
+ applyDecorFitsSystemWindows();
}
static private final String FOCUSED_ID_TAG = "android:focusedViewId";
@@ -3907,14 +3904,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
+ mDecorFitsSystemWindows = decorFitsSystemWindows;
+ applyDecorFitsSystemWindows();
+ }
+
+ private void applyDecorFitsSystemWindows() {
ViewRootImpl impl = getViewRootImplOrNull();
- OnContentApplyWindowInsetsListener listener = decorFitsSystemWindows
- ? sDefaultContentInsetsApplier
- : null;
if (impl != null) {
- impl.setOnContentApplyWindowInsetsListener(listener);
- } else {
- mPendingOnContentApplyWindowInsetsListener = listener;
+ impl.setOnContentApplyWindowInsetsListener(mDecorFitsSystemWindows
+ ? sDefaultContentInsetsApplier
+ : null);
}
}
}
diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java
index 3c9791791a68..9168438dc2bf 100644
--- a/core/java/com/android/internal/util/FunctionalUtils.java
+++ b/core/java/com/android/internal/util/FunctionalUtils.java
@@ -16,6 +16,7 @@
package com.android.internal.util;
+import android.annotation.NonNull;
import android.os.RemoteException;
import android.util.ExceptionUtils;
@@ -218,4 +219,37 @@ public class FunctionalUtils {
}
}
}
+
+ // TODO: add unit test
+ /**
+ * Gets a user-friendly name for a lambda function.
+ */
+ @NonNull
+ public static String getLambdaName(@NonNull Object function) {
+ // Full function has one of the following formats:
+ // package-$$Lambda$class$randomId
+ // package-$$Lambda$randomId
+ //
+ // We just want just package.class$Lambda (or package$Lambda) respectively
+
+ final String fullFunction = function.toString();
+
+ final int endPkgIdx = fullFunction.indexOf("-$$");
+ if (endPkgIdx == -1) return fullFunction;
+
+ // firstDollarIdx could be either beginning of class or beginning of the random id
+ final int firstDollarIdx = fullFunction.indexOf('$', endPkgIdx + 3);
+ if (firstDollarIdx == -1) return fullFunction;
+
+ final int endClassIdx = fullFunction.indexOf('$', firstDollarIdx + 1);
+ if (endClassIdx == -1) {
+ // Just package
+ return fullFunction.substring(0, endPkgIdx - 1) + "$Lambda";
+ }
+
+ // Package + class
+ return fullFunction.substring(0, endPkgIdx)
+ + fullFunction.substring(firstDollarIdx + 1, endClassIdx)
+ + "$Lambda";
+ }
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index e4a44084e91c..1646a07b8001 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -16,7 +16,6 @@
package com.android.internal.util.function.pooled;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Message;
import android.text.TextUtils;
@@ -25,6 +24,7 @@ import android.util.Pools;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
+import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.function.DecConsumer;
import com.android.internal.util.function.DecFunction;
import com.android.internal.util.function.DecPredicate;
@@ -580,36 +580,6 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
return r;
}
- // TODO: add unit test
- @NonNull
- private static String getFriendlyName(@NonNull Object function) {
- // Full function has one of the following formats:
- // package-$$Lambda$class$randomId
- // package-$$Lambda$randomId
- //
- // We just want just package.class$Lambda (or package$Lambda) respectively
-
- final String fullFunction = function.toString();
-
- final int endPkgIdx = fullFunction.indexOf("-$$");
- if (endPkgIdx == -1) return fullFunction;
-
- // firstDollarIdx could be either beginning of class or beginning of the random id
- final int firstDollarIdx = fullFunction.indexOf('$', endPkgIdx + 3);
- if (firstDollarIdx == -1) return fullFunction;
-
- final int endClassIdx = fullFunction.indexOf('$', firstDollarIdx + 1);
- if (endClassIdx == -1) {
- // Just package
- return fullFunction.substring(0, endPkgIdx - 1) + "$Lambda";
- }
-
- // Package + class
- return fullFunction.substring(0, endPkgIdx)
- + fullFunction.substring(firstDollarIdx + 1, endClassIdx)
- + "$Lambda";
- }
-
private static void setIfInBounds(Object[] array, int i, Object a) {
if (i < ArrayUtils.size(array)) array[i] = a;
}
@@ -651,7 +621,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
@Override
public String getTraceName() {
- return getFriendlyName(mFunc);
+ return FunctionalUtils.getLambdaName(mFunc);
}
private boolean isRecycled() {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 4b30359e671a..924dc4b3a051 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1653,7 +1653,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
uid, process_name, managed_nice_name, fail_fn);
isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
- if ((mount_external != MOUNT_EXTERNAL_INSTALLER) && mount_storage_dirs) {
+ if (mount_external != MOUNT_EXTERNAL_INSTALLER &&
+ mount_external != MOUNT_EXTERNAL_PASS_THROUGH &&
+ mount_storage_dirs) {
BindMountStorageDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 6321651c1345..3e007e4704a4 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2674,4 +2674,14 @@ enum PageId {
// CATEGORY: SETTINGS
// OS: R
FUELGAUGE_ADVANCED_BATTERY_OPTION = 1842;
+
+ // OPEN: Settings > System > Gestures > Power menu
+ // CATEGORY: SETTINGS
+ // OS: R
+ POWER_MENU_SETTINGS = 1843;
+
+ // OPEN: Settings > System > Gestures > Power menu > Device controls
+ // CATEGORY: SETTINGS
+ // OS: R
+ DEVICE_CONTROLS_SETTINGS = 1844;
}
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 684a29249294..896ee4fa42a5 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -186,4 +186,15 @@ enum EventId {
RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK= 159;
RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED= 160;
RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET = 161;
+ CROSS_PROFILE_SETTINGS_PAGE_LAUNCHED_FROM_APP = 162;
+ CROSS_PROFILE_SETTINGS_PAGE_LAUNCHED_FROM_SETTINGS = 163;
+ CROSS_PROFILE_SETTINGS_PAGE_ADMIN_RESTRICTED = 164;
+ CROSS_PROFILE_SETTINGS_PAGE_MISSING_WORK_APP = 165;
+ CROSS_PROFILE_SETTINGS_PAGE_MISSING_PERSONAL_APP = 166;
+ CROSS_PROFILE_SETTINGS_PAGE_MISSING_INSTALL_BANNER_INTENT = 167;
+ CROSS_PROFILE_SETTINGS_PAGE_INSTALL_BANNER_CLICKED = 168;
+ CROSS_PROFILE_SETTINGS_PAGE_INSTALL_BANNER_NO_INTENT_CLICKED = 169;
+ CROSS_PROFILE_SETTINGS_PAGE_USER_CONSENTED = 170;
+ CROSS_PROFILE_SETTINGS_PAGE_USER_DECLINED_CONSENT = 171;
+ CROSS_PROFILE_SETTINGS_PAGE_PERMISSION_REVOKED = 172;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 451363f6bd3d..1c978bff0fd3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -351,8 +351,9 @@
<protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_ALLOWED_APP" />
<protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISALLOWED_APP" />
<protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISMISSED" />
- <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_ALLOWED_CARRIER" />
- <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISALLOWED_CARRIER" />
+ <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_ALLOWED_CARRIER" />
+ <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISALLOWED_CARRIER" />
+ <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISMISSED" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
@@ -4122,7 +4123,10 @@
set of pages referenced over time.
<p>Declaring the permission implies intention to use the API and the user of the
device can grant permission through the Settings application.
- <p>Protection level: signature|privileged|appop -->
+ <p>Protection level: signature|privileged|appop
+ <p>A data loader has to be the one which provides data to install an app.
+ <p>A data loader has to have both permission:LOADER_USAGE_STATS AND
+ appop:LOADER_USAGE_STATS allowed to be able to access the read logs. -->
<permission android:name="android.permission.LOADER_USAGE_STATS"
android:protectionLevel="signature|privileged|appop" />
<uses-permission android:name="android.permission.LOADER_USAGE_STATS" />
@@ -5454,6 +5458,13 @@
</intent-filter>
</service>
+ <provider
+ android:name="com.android.server.textclassifier.IconsContentProvider"
+ android:authorities="com.android.textclassifier.icons"
+ android:enabled="true"
+ android:exported="true">
+ </provider>
+
</application>
</manifest>
diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java
new file mode 100644
index 000000000000..630e16ac80d4
--- /dev/null
+++ b/core/tests/coretests/src/android/app/WindowContextTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link WindowContext}
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksCoreTests:WindowContextTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@FlakyTest(bugId = 150812449, detail = "Remove after confirmed it's stable.")
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowContextTest {
+ @Test
+ public void testWindowContextRelease_doRemoveWindowToken() throws Throwable {
+ final Context instContext = InstrumentationRegistry.getInstrumentation()
+ .getTargetContext();
+ final Display display = instContext.getSystemService(DisplayManager.class)
+ .getDisplay(DEFAULT_DISPLAY);
+ final Context context = instContext.createDisplayContext(display);
+ final WindowContext windowContext = new WindowContext(context, TYPE_APPLICATION_OVERLAY,
+ null /* options */);
+
+ final IBinder token = windowContext.getActivityToken();
+
+ final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+ assertTrue("Token must be registered to WMS", wms.isWindowToken(token));
+
+ windowContext.release();
+
+ assertFalse("Token must be unregistered to WMS", wms.isWindowToken(token));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java
index 74524bf6d76f..ddc977d380ae 100644
--- a/core/tests/coretests/src/android/view/WindowMetricsTest.java
+++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java
@@ -56,17 +56,17 @@ public class WindowMetricsTest {
@Before
public void setUp() {
- final Context insetContext = InstrumentationRegistry.getInstrumentation()
+ final Context instContext = InstrumentationRegistry.getInstrumentation()
.getTargetContext();
- final Display display = insetContext.getSystemService(DisplayManager.class)
+ final Display display = instContext.getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY);
- mWindowContext = insetContext.createDisplayContext(display)
+ mWindowContext = instContext.createDisplayContext(display)
.createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
mWm = mWindowContext.getSystemService(WindowManager.class);
}
@Test
- public void testAddViewANdRemoveView_GetMetrics_DoNotCrash() {
+ public void testAddViewAndRemoveView_GetMetrics_DoNotCrash() {
final View view = new View(mWindowContext);
final WindowManager.LayoutParams params =
new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 59bdf3dad43e..0389639e9edb 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -137,6 +137,7 @@ applications that come with the platform
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
<permission name="android.permission.PACKAGE_USAGE_STATS" />
+ <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
</privapp-permissions>
<privapp-permissions package="com.android.phone">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 07cf41560cf3..19ad6f717ea5 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1507,6 +1507,18 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "838570988": {
+ "message": "Could not report token removal to the window token client.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowToken.java"
+ },
+ "845234215": {
+ "message": "App is requesting an orientation, return %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"853091290": {
"message": "Moved stack=%s behind stack=%s",
"level": "DEBUG",
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 415092623531..75ea0cbada92 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -47,10 +47,10 @@ interface ILocationManager
Location getLastLocation(in LocationRequest request, String packageName, String featureId);
boolean getCurrentLocation(in LocationRequest request,
in ICancellationSignal cancellationSignal, in ILocationListener listener,
- String packageName, String featureId);
+ String packageName, String featureId, String listenerId);
void requestLocationUpdates(in LocationRequest request, in ILocationListener listener,
- in PendingIntent intent, String packageName, String featureId);
+ in PendingIntent intent, String packageName, String featureId, String listenerId);
void removeUpdates(in ILocationListener listener, in PendingIntent intent);
void requestGeofence(in LocationRequest request, in Geofence geofence,
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index fcbd3e540291..d1b41dfccf63 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -718,7 +718,7 @@ public class LocationManager {
currentLocationRequest.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
}
- GetCurrentLocationTransport listenerTransport = new GetCurrentLocationTransport(executor,
+ GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor,
consumer);
if (cancellationSignal != null) {
@@ -729,14 +729,15 @@ public class LocationManager {
try {
if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal,
- listenerTransport, mContext.getPackageName(), mContext.getAttributionTag())) {
- listenerTransport.register(mContext.getSystemService(AlarmManager.class),
+ transport, mContext.getPackageName(), mContext.getAttributionTag(),
+ transport.getListenerId())) {
+ transport.register(mContext.getSystemService(AlarmManager.class),
remoteCancellationSignal);
if (cancellationSignal != null) {
- cancellationSignal.setOnCancelListener(listenerTransport::cancel);
+ cancellationSignal.setOnCancelListener(transport::cancel);
}
} else {
- listenerTransport.fail();
+ transport.fail();
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1175,7 +1176,8 @@ public class LocationManager {
boolean registered = false;
try {
mService.requestLocationUpdates(locationRequest, transport, null,
- mContext.getPackageName(), mContext.getAttributionTag());
+ mContext.getPackageName(), mContext.getAttributionTag(),
+ transport.getListenerId());
registered = true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1220,7 +1222,7 @@ public class LocationManager {
try {
mService.requestLocationUpdates(locationRequest, null, pendingIntent,
- mContext.getPackageName(), mContext.getAttributionTag());
+ mContext.getPackageName(), mContext.getAttributionTag(), null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2558,6 +2560,10 @@ public class LocationManager {
mRemoteCancellationSignal = null;
}
+ public String getListenerId() {
+ return mConsumer.getClass().getName() + "@" + System.identityHashCode(mConsumer);
+ }
+
public synchronized void register(AlarmManager alarmManager,
ICancellationSignal remoteCancellationSignal) {
if (mConsumer == null) {
@@ -2683,6 +2689,10 @@ public class LocationManager {
return mListener;
}
+ public String getListenerId() {
+ return mListener.getClass().getName() + "@" + System.identityHashCode(mListener);
+ }
+
public void register(@NonNull Executor executor) {
Preconditions.checkArgument(executor != null, "invalid null executor");
mExecutor = executor;
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 4dd1a29d8595..5f0acc8f7647 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -264,8 +264,8 @@ public final class LocationRequest implements Parcelable {
/* numUpdates= */ Integer.MAX_VALUE,
/* smallestDisplacement= */ 0,
/* hideFromAppOps= */ false,
- /* lowPowerMode= */ false,
/* locationSettingsIgnored= */ false,
+ /* lowPowerMode= */ false,
/* workSource= */ null);
}
@@ -282,8 +282,8 @@ public final class LocationRequest implements Parcelable {
src.mNumUpdates,
src.mSmallestDisplacement,
src.mHideFromAppOps,
- src.mLowPowerMode,
src.mLocationSettingsIgnored,
+ src.mLowPowerMode,
src.mWorkSource);
}
diff --git a/packages/CarSystemUI/res/layout/notification_center_activity.xml b/packages/CarSystemUI/res/layout/notification_center_activity.xml
index 4fef48918c97..0af74c4462a6 100644
--- a/packages/CarSystemUI/res/layout/notification_center_activity.xml
+++ b/packages/CarSystemUI/res/layout/notification_center_activity.xml
@@ -20,8 +20,6 @@
android:id="@+id/notification_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="invisible"
- android:layout_marginBottom="@dimen/navigation_bar_height"
android:background="@color/notification_shade_background_color">
<View
diff --git a/packages/CarSystemUI/res/layout/notification_panel_container.xml b/packages/CarSystemUI/res/layout/notification_panel_container.xml
index bf71396984b8..3b53c6aaeac3 100644
--- a/packages/CarSystemUI/res/layout/notification_panel_container.xml
+++ b/packages/CarSystemUI/res/layout/notification_panel_container.xml
@@ -18,4 +18,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notification_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ android:layout_height="match_parent"
+ android:visibility="invisible"/>
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 1b0a211b733d..067e359e0e49 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -22,10 +22,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <!-- TODO(b/151617493): replace marginBottom with insets. -->
<ViewStub android:id="@+id/notification_panel_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout="@layout/notification_panel_container"/>
+ android:layout="@layout/notification_panel_container"
+ android:layout_marginBottom="@dimen/navigation_bar_height"/>
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java
index b057198d5177..44e43fe9af8c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java
@@ -24,7 +24,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
*/
public interface CarDeviceProvisionedController extends DeviceProvisionedController {
/**
- * Returns {@code true} then SUW is in progress for the given user.
+ * Returns {@code true} when SUW is in progress for the given user.
*/
boolean isUserSetupInProgress(int user);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 1901a2db879d..d8a894cfa8ba 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -16,9 +16,6 @@
package com.android.systemui.car.notification;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.car.Car;
import android.car.drivingstate.CarUxRestrictionsManager;
@@ -33,7 +30,6 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
@@ -54,7 +50,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.window.OverlayViewController;
+import com.android.systemui.window.OverlayPanelViewController;
import com.android.systemui.window.OverlayViewGlobalStateController;
import javax.inject.Inject;
@@ -62,39 +58,22 @@ import javax.inject.Singleton;
/** View controller for the notification panel. */
@Singleton
-public class NotificationPanelViewController extends OverlayViewController {
-
- // used to calculate how fast to open or close the window
- private static final float DEFAULT_FLING_VELOCITY = 0;
- // max time a fling animation takes
- private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
- // acceleration rate for the fling animation
- private static final float FLING_SPEED_UP_FACTOR = 0.6f;
-
- private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
- private static final int SWIPE_MAX_OFF_PATH = 75;
- private static final int SWIPE_THRESHOLD_VELOCITY = 200;
+public class NotificationPanelViewController extends OverlayPanelViewController {
+
private static final boolean DEBUG = true;
private static final String TAG = "NotificationPanelViewController";
private final Context mContext;
private final Resources mResources;
private final CarServiceProvider mCarServiceProvider;
- private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final IStatusBarService mBarService;
private final CommandQueue mCommandQueue;
private final NotificationDataManager mNotificationDataManager;
private final CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper;
private final CarNotificationListener mCarNotificationListener;
private final NotificationClickHandlerFactory mNotificationClickHandlerFactory;
- private final FlingAnimationUtils mFlingAnimationUtils;
private final StatusBarStateController mStatusBarStateController;
- private final int mSettleClosePercentage;
-
- private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
- private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
-
private float mInitialBackgroundAlpha;
private float mBackgroundAlphaDiff;
@@ -108,13 +87,7 @@ public class NotificationPanelViewController extends OverlayViewController {
private float mFirstTouchDownOnGlassPane;
private boolean mNotificationListAtBottomAtTimeOfTouch;
private boolean mIsSwipingVerticallyToClose;
- private int mPercentageFromBottom;
- private boolean mIsNotificationAnimating;
private boolean mIsNotificationCardSwiping;
- private boolean mPanelExpanded = false;
-
- private View.OnTouchListener mTopNavBarNotificationTouchListener;
- private View.OnTouchListener mNavBarNotificationTouchListener;
private OnUnseenCountUpdateListener mUnseenCountUpdateListener;
@@ -123,6 +96,7 @@ public class NotificationPanelViewController extends OverlayViewController {
Context context,
@Main Resources resources,
OverlayViewGlobalStateController overlayViewGlobalStateController,
+ FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
/* Other things */
CarServiceProvider carServiceProvider,
@@ -135,26 +109,21 @@ public class NotificationPanelViewController extends OverlayViewController {
CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
CarNotificationListener carNotificationListener,
NotificationClickHandlerFactory notificationClickHandlerFactory,
- FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
/* Things that need to be replaced */
StatusBarStateController statusBarStateController
) {
- super(R.id.notification_panel_stub, overlayViewGlobalStateController);
+ super(context, resources, R.id.notification_panel_stub, overlayViewGlobalStateController,
+ flingAnimationUtilsBuilder, carDeviceProvisionedController);
mContext = context;
mResources = resources;
mCarServiceProvider = carServiceProvider;
- mCarDeviceProvisionedController = carDeviceProvisionedController;
mBarService = barService;
mCommandQueue = commandQueue;
mNotificationDataManager = notificationDataManager;
mCarUxRestrictionManagerWrapper = carUxRestrictionManagerWrapper;
mCarNotificationListener = carNotificationListener;
mNotificationClickHandlerFactory = notificationClickHandlerFactory;
- mFlingAnimationUtils = flingAnimationUtilsBuilder
- .setMaxLengthSeconds(FLING_ANIMATION_MAX_TIME)
- .setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
- .build();
mStatusBarStateController = statusBarStateController;
// Notification background setup.
@@ -175,60 +144,6 @@ public class NotificationPanelViewController extends OverlayViewController {
+ " percentage");
}
mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha;
-
- // Notification Panel param setup
- mSettleClosePercentage = mResources.getInteger(
- R.integer.notification_settle_close_percentage);
-
- // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
- // notification shade.
- GestureDetector openGestureDetector = new GestureDetector(mContext,
- new OpenNotificationGestureListener() {
- @Override
- protected void openNotification() {
- animateExpandNotificationsPanel();
- }
- });
-
- // Attached to the NavBars to close the notification shade
- GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext,
- new NavBarCloseNotificationGestureListener() {
- @Override
- protected void close() {
- if (mPanelExpanded) {
- animateCollapsePanels();
- }
- }
- });
-
- mTopNavBarNotificationTouchListener = (v, event) -> {
- if (!isInflated()) {
- getOverlayViewGlobalStateController().inflateView(this);
- }
- if (!mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
- return true;
- }
-
- boolean consumed = openGestureDetector.onTouchEvent(event);
- if (consumed) {
- return true;
- }
- maybeCompleteAnimation(event);
- return true;
- };
-
- mNavBarNotificationTouchListener =
- (v, event) -> {
- if (!isInflated()) {
- return true;
- }
- boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
- if (consumed) {
- return true;
- }
- maybeCompleteAnimation(event);
- return true;
- };
}
@Override
@@ -252,14 +167,13 @@ public class NotificationPanelViewController extends OverlayViewController {
private void onNotificationViewInflated() {
// Find views.
mNotificationView = getLayout().findViewById(R.id.notification_view);
- View glassPane = mNotificationView.findViewById(R.id.glass_pane);
- mHandleBar = mNotificationView.findViewById(R.id.handle_bar);
- mNotificationList = mNotificationView.findViewById(R.id.notifications);
+ setupHandleBar();
+ setupNotificationPanel();
mNotificationClickHandlerFactory.registerClickListener((launchResult, alertEntry) -> {
if (launchResult == ActivityManager.START_TASK_TO_FRONT
|| launchResult == ActivityManager.START_SUCCESS) {
- animateCollapsePanels();
+ animateCollapsePanel();
}
});
@@ -269,39 +183,52 @@ public class NotificationPanelViewController extends OverlayViewController {
mNotificationDataManager.getUnseenNotificationCount());
}
});
+
mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
mNotificationView.setClickHandlerFactory(mNotificationClickHandlerFactory);
mNotificationView.setNotificationDataManager(mNotificationDataManager);
- mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
- super.onScrolled(recyclerView, dx, dy);
- if (!mNotificationList.canScrollVertically(1)) {
- mNotificationListAtBottom = true;
- return;
- }
- mNotificationListAtBottom = false;
- mIsSwipingVerticallyToClose = false;
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
+ mCarServiceProvider.addListener(car -> {
+ CarUxRestrictionsManager carUxRestrictionsManager =
+ (CarUxRestrictionsManager)
+ car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+ mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager(
+ carUxRestrictionsManager);
+
+ mNotificationViewController = new NotificationViewController(
+ mNotificationView,
+ PreprocessingManager.getInstance(mContext),
+ mCarNotificationListener,
+ mCarUxRestrictionManagerWrapper,
+ mNotificationDataManager);
+ mNotificationViewController.enable();
+ });
+ }
+
+ private void setupHandleBar() {
+ mHandleBar = mNotificationView.findViewById(R.id.handle_bar);
+ GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
+ new HandleBarCloseGestureListener());
+ mHandleBar.setOnTouchListener((v, event) -> {
+ handleBarCloseNotificationGestureDetector.onTouchEvent(event);
+ maybeCompleteAnimation(event);
+ return true;
});
+ }
- // Attached to the notification ui to detect close request of the notification shade.
+ private void setupNotificationPanel() {
+ View glassPane = mNotificationView.findViewById(R.id.glass_pane);
+ mNotificationList = mNotificationView.findViewById(R.id.notifications);
GestureDetector closeGestureDetector = new GestureDetector(mContext,
- new CloseNotificationGestureListener() {
+ new CloseGestureListener() {
@Override
protected void close() {
- if (mPanelExpanded) {
- animateCollapsePanels();
+ if (isPanelExpanded()) {
+ animateCollapsePanel();
}
}
});
- // Attached to the Handle bar to close the notification shade
- GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
- new HandleBarCloseNotificationGestureListener());
-
// The glass pane is used to view touch events before passed to the notification list.
// This allows us to initialize gesture listeners and detect when to close the notifications
glassPane.setOnTouchListener((v, event) -> {
@@ -320,6 +247,21 @@ public class NotificationPanelViewController extends OverlayViewController {
return false;
});
+ mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+ // Check if we can scroll vertically downwards.
+ if (!mNotificationList.canScrollVertically(/* direction= */ 1)) {
+ mNotificationListAtBottom = true;
+ return;
+ }
+ mNotificationListAtBottom = false;
+ mIsSwipingVerticallyToClose = false;
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+ });
+
mNotificationList.setOnTouchListener((v, event) -> {
mIsNotificationCardSwiping = Math.abs(mFirstTouchDownOnGlassPane - event.getRawX())
> SWIPE_MAX_OFF_PATH;
@@ -341,19 +283,19 @@ public class NotificationPanelViewController extends OverlayViewController {
boolean handled = closeGestureDetector.onTouchEvent(event);
boolean isTracking = mIsTracking;
- Rect rect = mNotificationView.getClipBounds();
+ Rect rect = getLayout().getClipBounds();
float clippedHeight = 0;
if (rect != null) {
clippedHeight = rect.bottom;
}
if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP
&& mIsSwipingVerticallyToClose) {
- if (mSettleClosePercentage < mPercentageFromBottom && isTracking) {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
- } else if (clippedHeight != mNotificationView.getHeight() && isTracking) {
+ if (getSettleClosePercentage() < getPercentageFromBottom() && isTracking) {
+ animatePanel(DEFAULT_FLING_VELOCITY, false);
+ } else if (clippedHeight != getLayout().getHeight() && isTracking) {
// this can be caused when user is at the end of the list and trying to
// fling to top of the list by scrolling down.
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
+ animatePanel(DEFAULT_FLING_VELOCITY, true);
}
}
@@ -365,28 +307,6 @@ public class NotificationPanelViewController extends OverlayViewController {
}
return handled || isTracking;
});
-
- mCarServiceProvider.addListener(car -> {
- CarUxRestrictionsManager carUxRestrictionsManager =
- (CarUxRestrictionsManager)
- car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
- mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager(
- carUxRestrictionsManager);
-
- mNotificationViewController = new NotificationViewController(
- mNotificationView,
- PreprocessingManager.getInstance(mContext),
- mCarNotificationListener,
- mCarUxRestrictionManagerWrapper,
- mNotificationDataManager);
- mNotificationViewController.enable();
- });
-
- mHandleBar.setOnTouchListener((v, event) -> {
- handleBarCloseNotificationGestureDetector.onTouchEvent(event);
- maybeCompleteAnimation(event);
- return true;
- });
}
/** Called when the car power state is changed to ON. */
@@ -397,139 +317,40 @@ public class NotificationPanelViewController extends OverlayViewController {
mNotificationDataManager.clearAll();
}
- View.OnTouchListener getTopNavBarNotificationTouchListener() {
- return mTopNavBarNotificationTouchListener;
- }
-
- View.OnTouchListener getNavBarNotificationTouchListener() {
- return mNavBarNotificationTouchListener;
- }
-
- private void maybeCompleteAnimation(MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_UP
- && mNotificationView.getVisibility() == View.VISIBLE) {
- if (mSettleClosePercentage < mPercentageFromBottom) {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
- } else {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
- }
- }
+ @Override
+ protected boolean shouldAnimateCollapsePanel() {
+ return true;
}
- /**
- * Animates the notification shade from one position to other. This is used to either open or
- * close the notification shade completely with a velocity. If the animation is to close the
- * notification shade this method also makes the view invisible after animation ends.
- */
- private void animateNotificationPanel(float velocity, boolean isClosing) {
- float to = 0;
- if (!isClosing) {
- to = mNotificationView.getHeight();
- }
-
- Rect rect = mNotificationView.getClipBounds();
- if (rect != null && rect.bottom != to) {
- float from = rect.bottom;
- animate(from, to, velocity, isClosing);
- return;
- }
-
- // We will only be here if the shade is being opened programmatically or via button when
- // height of the layout was not calculated.
- ViewTreeObserver notificationTreeObserver = mNotificationView.getViewTreeObserver();
- notificationTreeObserver.addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- ViewTreeObserver obs = mNotificationView.getViewTreeObserver();
- obs.removeOnGlobalLayoutListener(this);
- float to = mNotificationView.getHeight();
- animate(/* from= */ 0, to, velocity, isClosing);
- }
- });
+ @Override
+ protected void onAnimateCollapsePanel() {
+ // No op.
}
- private void animateCollapsePanels() {
- if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) {
- return;
- }
- getOverlayViewGlobalStateController().setWindowFocusable(false);
- animateNotificationPanel(mClosingVelocity, true);
+ @Override
+ protected boolean shouldAnimateExpandPanel() {
+ return mCommandQueue.panelsEnabled();
}
- private void animateExpandNotificationsPanel() {
- if (!mCommandQueue.panelsEnabled()
- || !mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
- return;
- }
- // scroll to top
+ @Override
+ protected void onAnimateExpandPanel() {
mNotificationList.scrollToPosition(0);
- setPanelVisible(true);
- mNotificationView.setVisibility(View.VISIBLE);
- animateNotificationPanel(mOpeningVelocity, false);
-
- setPanelExpanded(true);
}
- private void animate(float from, float to, float velocity, boolean isClosing) {
- if (mIsNotificationAnimating) {
- return;
- }
- mIsNotificationAnimating = true;
- mIsTracking = true;
- ValueAnimator animator = ValueAnimator.ofFloat(from, to);
- animator.addUpdateListener(
- animation -> {
- float animatedValue = (Float) animation.getAnimatedValue();
- setNotificationViewClipBounds((int) animatedValue);
- });
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- mIsNotificationAnimating = false;
- mIsTracking = false;
- mOpeningVelocity = DEFAULT_FLING_VELOCITY;
- mClosingVelocity = DEFAULT_FLING_VELOCITY;
- if (isClosing) {
- setPanelVisible(false);
- mNotificationView.setVisibility(View.INVISIBLE);
- mNotificationView.setClipBounds(null);
- mNotificationViewController.onVisibilityChanged(false);
- // let the status bar know that the panel is closed
- setPanelExpanded(false);
- } else {
- mNotificationViewController.onVisibilityChanged(true);
- // let the status bar know that the panel is open
- mNotificationView.setVisibleNotificationsAsSeen();
- setPanelExpanded(true);
- }
- }
- });
- mFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity));
- animator.start();
+ @Override
+ protected void onCollapseAnimationEnd() {
+ mNotificationViewController.onVisibilityChanged(false);
}
- /**
- * Set the panel view to be visible.
- */
- public void setPanelVisible(boolean visible) {
- if (visible && !getOverlayViewGlobalStateController().isWindowVisible()) {
- getOverlayViewGlobalStateController().setWindowVisible(true);
- }
- if (!visible && getOverlayViewGlobalStateController().isWindowVisible()) {
- getOverlayViewGlobalStateController().setWindowVisible(false);
- }
- getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- getOverlayViewGlobalStateController().setWindowFocusable(visible);
+ @Override
+ protected void onExpandAnimationEnd() {
+ mNotificationViewController.onVisibilityChanged(true);
+ mNotificationView.setVisibleNotificationsAsSeen();
}
- /**
- * Set the panel state to expanded. This will expand or collapse the overlay window if
- * necessary.
- */
- public void setPanelExpanded(boolean expand) {
- mPanelExpanded = expand;
+ @Override
+ protected void onPanelExpanded(boolean expand) {
+ super.onPanelExpanded(expand);
if (expand && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
if (DEBUG) {
@@ -550,19 +371,19 @@ public class NotificationPanelViewController extends OverlayViewController {
}
}
- private void setNotificationViewClipBounds(int height) {
- if (height > mNotificationView.getHeight()) {
- height = mNotificationView.getHeight();
- }
- Rect clipBounds = new Rect();
- clipBounds.set(0, 0, mNotificationView.getWidth(), height);
- // Sets the clip region on the notification list view.
- mNotificationView.setClipBounds(clipBounds);
+ @Override
+ protected void onOpenScrollStart() {
+ mNotificationList.scrollToPosition(0);
+ }
+
+ @Override
+ protected void onScroll(int height) {
if (mHandleBar != null) {
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) mHandleBar.getLayoutParams();
mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin);
}
+
if (mNotificationView.getHeight() > 0) {
Drawable background = mNotificationView.getBackground().mutate();
background.setAlpha((int) (getBackgroundAlpha(height) * 255));
@@ -570,6 +391,13 @@ public class NotificationPanelViewController extends OverlayViewController {
}
}
+ @Override
+ protected boolean shouldAllowClosingScroll() {
+ // Unless the notification list is at the bottom, the panel shouldn't be allowed to
+ // collapse on scroll.
+ return mNotificationListAtBottomAtTimeOfTouch;
+ }
+
/**
* Calculates the alpha value for the background based on how much of the notification
* shade is visible to the user. When the notification shade is completely open then
@@ -580,30 +408,6 @@ public class NotificationPanelViewController extends OverlayViewController {
+ ((float) height / mNotificationView.getHeight() * mBackgroundAlphaDiff);
}
- private void calculatePercentageFromBottom(float height) {
- if (mNotificationView.getHeight() > 0) {
- mPercentageFromBottom = (int) Math.abs(
- height / mNotificationView.getHeight() * 100);
- }
- }
-
- /** Toggles the visibility of the notification panel. */
- public void toggle() {
- if (!isInflated()) {
- getOverlayViewGlobalStateController().inflateView(this);
- }
- if (mPanelExpanded) {
- animateCollapsePanels();
- } else {
- animateExpandNotificationsPanel();
- }
- }
-
- /** Returns {@code true} if the notification panel is expanded. */
- public boolean isPanelExpanded() {
- return mPanelExpanded;
- }
-
/** Sets the unseen count listener. */
public void setOnUnseenCountUpdateListener(OnUnseenCountUpdateListener listener) {
mUnseenCountUpdateListener = listener;
@@ -619,154 +423,9 @@ public class NotificationPanelViewController extends OverlayViewController {
}
/**
- * Only responsible for open hooks. Since once the panel opens it covers all elements
- * there is no need to merge with close.
- */
- private abstract class OpenNotificationGestureListener extends
- GestureDetector.SimpleOnGestureListener {
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
-
- if (mNotificationView.getVisibility() == View.INVISIBLE) {
- // when the on-scroll is called for the first time to open.
- mNotificationList.scrollToPosition(0);
- }
- setPanelVisible(true);
- mNotificationView.setVisibility(View.VISIBLE);
-
- // clips the view for the notification shade when the user scrolls to open.
- setNotificationViewClipBounds((int) event2.getRawY());
-
- // Initially the scroll starts with height being zero. This checks protects from divide
- // by zero error.
- calculatePercentageFromBottom(event2.getRawY());
-
- mIsTracking = true;
- return true;
- }
-
-
- @Override
- public boolean onFling(MotionEvent event1, MotionEvent event2,
- float velocityX, float velocityY) {
- if (velocityY > SWIPE_THRESHOLD_VELOCITY) {
- mOpeningVelocity = velocityY;
- openNotification();
- return true;
- }
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
-
- return false;
- }
-
- protected abstract void openNotification();
- }
-
- /**
- * To be installed on the open panel notification panel
- */
- private abstract class CloseNotificationGestureListener extends
- GestureDetector.SimpleOnGestureListener {
-
- @Override
- public boolean onSingleTapUp(MotionEvent motionEvent) {
- if (mPanelExpanded) {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
- }
- return true;
- }
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
- // should not clip while scroll to the bottom of the list.
- if (!mNotificationListAtBottomAtTimeOfTouch) {
- return false;
- }
- float actualNotificationHeight =
- mNotificationView.getHeight() - (event1.getRawY() - event2.getRawY());
- if (actualNotificationHeight > mNotificationView.getHeight()) {
- actualNotificationHeight = mNotificationView.getHeight();
- }
- if (mNotificationView.getHeight() > 0) {
- mPercentageFromBottom = (int) Math.abs(
- actualNotificationHeight / mNotificationView.getHeight() * 100);
- boolean isUp = distanceY > 0;
-
- // This check is to figure out if onScroll was called while swiping the card at
- // bottom of the list. At that time we should not allow notification shade to
- // close. We are also checking for the upwards swipe gesture here because it is
- // possible if a user is closing the notification shade and while swiping starts
- // to open again but does not fling. At that time we should allow the
- // notification shade to close fully or else it would stuck in between.
- if (Math.abs(mNotificationView.getHeight() - actualNotificationHeight)
- > SWIPE_DOWN_MIN_DISTANCE && isUp) {
- setNotificationViewClipBounds((int) actualNotificationHeight);
- mIsTracking = true;
- } else if (!isUp) {
- setNotificationViewClipBounds((int) actualNotificationHeight);
- }
- }
- // if we return true the items in RV won't be scrollable.
- return false;
- }
-
-
- @Override
- public boolean onFling(MotionEvent event1, MotionEvent event2,
- float velocityX, float velocityY) {
- // should not fling if the touch does not start when view is at the bottom of the list.
- if (!mNotificationListAtBottomAtTimeOfTouch) {
- return false;
- }
- if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
- || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
- // swipe was not vertical or was not fast enough
- return false;
- }
- boolean isUp = velocityY < 0;
- if (isUp) {
- close();
- return true;
- } else {
- // we should close the shade
- animateNotificationPanel(velocityY, false);
- }
- return false;
- }
-
- protected abstract void close();
- }
-
- /**
- * To be installed on the nav bars.
- */
- private abstract class NavBarCloseNotificationGestureListener extends
- CloseNotificationGestureListener {
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- mClosingVelocity = DEFAULT_FLING_VELOCITY;
- if (mPanelExpanded) {
- close();
- }
- return super.onSingleTapUp(e);
- }
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
- calculatePercentageFromBottom(event2.getRawY());
- setNotificationViewClipBounds((int) event2.getRawY());
- return true;
- }
- }
-
- /**
* To be installed on the handle bar.
*/
- private class HandleBarCloseNotificationGestureListener extends
+ private class HandleBarCloseGestureListener extends
GestureDetector.SimpleOnGestureListener {
@Override
@@ -777,9 +436,8 @@ public class NotificationPanelViewController extends OverlayViewController {
// the handle bar we should calculate the height using the diff of event1 and event2.
// This will help the notification shade to clip smoothly as the event2 value changes
// as event1 value will be fixed.
- int clipHeight =
- mNotificationView.getHeight() - (int) (event1.getRawY() - event2.getRawY());
- setNotificationViewClipBounds(clipHeight);
+ int clipHeight = getLayout().getHeight() - (int) (event1.getRawY() - event2.getRawY());
+ setViewClipBounds(clipHeight);
return true;
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
index 110c2ee8854b..9d71797794b8 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
@@ -59,13 +59,13 @@ public class NotificationPanelViewMediator implements OverlayViewMediator,
@Override
public void registerListeners() {
mCarNavigationBarController.registerTopBarTouchListener(
- mNotificationPanelViewController.getTopNavBarNotificationTouchListener());
+ mNotificationPanelViewController.getDragOpenTouchListener());
mCarNavigationBarController.registerBottomBarTouchListener(
- mNotificationPanelViewController.getNavBarNotificationTouchListener());
+ mNotificationPanelViewController.getDragCloseTouchListener());
mCarNavigationBarController.registerLeftBarTouchListener(
- mNotificationPanelViewController.getNavBarNotificationTouchListener());
+ mNotificationPanelViewController.getDragCloseTouchListener());
mCarNavigationBarController.registerRightBarTouchListener(
- mNotificationPanelViewController.getNavBarNotificationTouchListener());
+ mNotificationPanelViewController.getDragCloseTouchListener());
mCarNavigationBarController.registerNotificationController(
new CarNavigationBarController.NotificationsShadeController() {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarTrustAgentUnlockDialogHelper.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarTrustAgentUnlockDialogHelper.java
deleted file mode 100644
index 597716569e9b..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarTrustAgentUnlockDialogHelper.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car.userswitcher;
-
-import android.app.admin.DevicePolicyManager;
-import android.bluetooth.BluetoothAdapter;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Main;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * A helper class displays an unlock dialog and receives broadcast about detecting trusted device
- * & unlocking state to show the appropriate message on the dialog.
- */
-@Singleton
-class CarTrustAgentUnlockDialogHelper extends BroadcastReceiver{
- private static final String TAG = CarTrustAgentUnlockDialogHelper.class.getSimpleName();
-
- private final Context mContext;
- private final Resources mResources;
- private final WindowManager mWindowManager;
- private final UserManager mUserManager;
- private final WindowManager.LayoutParams mParams;
- /**
- * Not using Dialog because context passed from {@link FullscreenUserSwitcherViewMediator}
- * is not an activity.
- */
- private final View mUnlockDialogLayout;
- private final TextView mUnlockingText;
- private final Button mButton;
- private final IntentFilter mFilter;
- private int mUid;
- private boolean mIsDialogShowing;
- private OnHideListener mOnHideListener;
-
- @Inject
- CarTrustAgentUnlockDialogHelper(Context context, @Main Resources resources,
- UserManager userManager, WindowManager windowManager) {
- mContext = context;
- mResources = resources;
- mUserManager = userManager;
- mWindowManager = windowManager;
- mParams = createLayoutParams();
- mFilter = getIntentFilter();
-
- mParams.packageName = mContext.getPackageName();
- mParams.setTitle(mContext.getString(R.string.unlock_dialog_title));
-
- mUnlockDialogLayout = LayoutInflater.from(mContext).inflate(
- R.layout.trust_agent_unlock_dialog, null);
- mUnlockDialogLayout.setLayoutParams(mParams);
-
- View dialogParent = mUnlockDialogLayout.findViewById(R.id.unlock_dialog_parent);
- dialogParent.setOnTouchListener((v, event)-> {
- hideUnlockDialog(/* dismissUserSwitcher= */ false);
- return true;
- });
- View unlockDialog = mUnlockDialogLayout.findViewById(R.id.unlock_dialog);
- unlockDialog.setOnTouchListener((v, event) -> {
- // If the person taps inside the unlock dialog, the touch event will be intercepted here
- // and the dialog will not exit
- return true;
- });
- mUnlockingText = mUnlockDialogLayout.findViewById(R.id.unlocking_text);
- mButton = mUnlockDialogLayout.findViewById(R.id.enter_pin_button);
- mButton.setOnClickListener(v -> {
- hideUnlockDialog(/* dismissUserSwitcher= */true);
- // TODO(b/138250105) Stop unlock advertising
- });
-
- BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- if (bluetoothAdapter != null
- && bluetoothAdapter.getLeState() == BluetoothAdapter.STATE_BLE_ON) {
- mUnlockingText.setText(R.string.unlock_dialog_message_start);
- }
- }
-
- /**
- * This filter is listening on:
- * {@link BluetoothAdapter#ACTION_BLE_STATE_CHANGED} for starting unlock advertising;
- * {@link Intent#ACTION_USER_UNLOCKED} for IHU unlocked
- */
- private IntentFilter getIntentFilter() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
- filter.addAction(Intent.ACTION_USER_UNLOCKED);
- return filter;
- }
-
- /**
- * Show dialog for the given user
- */
- void showUnlockDialog(int uid, OnHideListener listener) {
- showUnlockDialogAfterDelay(uid, 0, listener);
- }
-
- /**
- * Show dialog for the given user after the certain time of delay has elapsed
- *
- * @param uid the user to unlock
- * @param listener listener that listens to dialog hide
- */
- void showUnlockDialogAfterDelay(int uid, OnHideListener listener) {
- long delayMillis = mResources.getInteger(R.integer.unlock_dialog_delay_ms);
- showUnlockDialogAfterDelay(uid, delayMillis, listener);
- }
-
- /**
- * Show dialog for the given user after the supplied delay has elapsed
- */
- private void showUnlockDialogAfterDelay(int uid, long delayMillis, OnHideListener listener) {
- setUid(uid);
- mOnHideListener = listener;
- if (!mIsDialogShowing) {
- logd("Receiver registered");
- mContext.registerReceiverAsUser(this, UserHandle.ALL, mFilter,
- /* broadcastPermission= */ null,
- /* scheduler= */ null);
- new Handler().postDelayed(() -> {
- if (!mUserManager.isUserUnlocked(uid)) {
- logd("Showed unlock dialog for user: " + uid + " after " + delayMillis
- + " delay.");
- mWindowManager.addView(mUnlockDialogLayout, mParams);
- }
- }, delayMillis);
- }
- mIsDialogShowing = true;
- }
-
- private void setUid(int uid) {
- mUid = uid;
- TextView userName = mUnlockDialogLayout.findViewById(R.id.user_name);
- userName.setText(mUserManager.getUserInfo(mUid).name);
- ImageView avatar = mUnlockDialogLayout.findViewById(R.id.avatar);
- avatar.setImageBitmap(mUserManager.getUserIcon(mUid));
- setButtonText();
- }
-
- private void hideUnlockDialog(boolean dismissUserSwitcher) {
- if (!mIsDialogShowing) {
- return;
- }
- mWindowManager.removeView(mUnlockDialogLayout);
- logd("Receiver unregistered");
- mContext.unregisterReceiver(this);
- if (mOnHideListener != null) {
- mOnHideListener.onHide(dismissUserSwitcher);
- }
- mIsDialogShowing = false;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action == null) {
- return;
- }
- switch (action) {
- case BluetoothAdapter.ACTION_BLE_STATE_CHANGED:
- logd("Received ACTION_BLE_STATE_CHANGED");
- int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
- if (state == BluetoothAdapter.STATE_BLE_ON) {
- logd("Received BLE_ON");
- mUnlockingText.setText(R.string.unlock_dialog_message_start);
- }
- break;
- case Intent.ACTION_USER_UNLOCKED:
- int uid = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (uid == mUid) {
- logd("IHU unlocked");
- hideUnlockDialog(/* notifyOnHideListener= */false);
- } else {
- Log.e(TAG, "Received ACTION_USER_UNLOCKED for unexpected uid: " + uid);
- }
- break;
- default:
- Log.e(TAG, "Encountered unexpected action when attempting to set "
- + "unlock state message: " + action);
- }
- }
-
- // Set button text based on screen lock type
- private void setButtonText() {
- LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
- int passwordQuality = lockPatternUtils.getActivePasswordQuality(mUid);
- switch (passwordQuality) {
- // PIN
- case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
- case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
- mButton.setText(R.string.unlock_dialog_button_text_pin);
- break;
- // Pattern
- case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
- mButton.setText(R.string.unlock_dialog_button_text_pattern);
- break;
- // Password
- case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
- case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
- case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
- mButton.setText(R.string.unlock_dialog_button_text_password);
- break;
- default:
- Log.e(TAG, "Encountered unexpected screen lock type when attempting to set "
- + "button text:" + passwordQuality);
- }
- }
-
- private WindowManager.LayoutParams createLayoutParams() {
- final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
- WindowManager.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
- WindowManager.LayoutParams.FLAG_FULLSCREEN
- | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
- | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
- PixelFormat.TRANSLUCENT
- );
- attrs.setFitInsetsTypes(0 /* types */);
- return attrs;
- }
-
- private void logd(String message) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, message);
- }
- }
-
- /**
- * Listener used to notify when the dialog is hidden
- */
- interface OnHideListener {
- void onHide(boolean dismissUserSwitcher);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java
index 627729768e88..50e43bea65eb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java
@@ -16,23 +16,14 @@
package com.android.systemui.car.userswitcher;
-import android.car.Car;
-import android.car.trust.CarTrustAgentEnrollmentManager;
-import android.car.userlib.CarUserManagerHelper;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.os.Handler;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
-import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -52,9 +43,6 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
private static final String TAG = FullscreenUserSwitcherViewMediator.class.getSimpleName();
private final Context mContext;
- private final UserManager mUserManager;
- private final CarServiceProvider mCarServiceProvider;
- private final CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
private final CarStatusBarKeyguardViewManager mCarStatusBarKeyguardViewManager;
private final Handler mMainHandler;
private final StatusBarStateController mStatusBarStateController;
@@ -62,39 +50,12 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
private final ScreenLifecycle mScreenLifecycle;
private final CarStatusBar mCarStatusBar;
private final boolean mIsUserSwitcherEnabled;
- private final CarUserManagerHelper mCarUserManagerHelper;
-
- private CarTrustAgentEnrollmentManager mEnrollmentManager;
- private UserGridRecyclerView.UserRecord mSelectedUser;
- private final CarTrustAgentUnlockDialogHelper.OnHideListener mOnHideListener =
- dismissUserSwitcher -> {
- if (dismissUserSwitcher) {
- dismissUserSwitcher();
- } else {
- // Re-draw the parent view, otherwise the unlock dialog will not be removed
- // from the screen immediately.
- invalidateFullscreenUserSwitcherView();
- }
- };
- private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "user 0 is unlocked, SharedPreference is accessible.");
- }
- showDialogForInitialUser();
- mContext.unregisterReceiver(mUserUnlockReceiver);
- }
- };
@Inject
public FullscreenUserSwitcherViewMediator(
Context context,
@Main Resources resources,
@Main Handler mainHandler,
- UserManager userManager,
- CarServiceProvider carServiceProvider,
- CarTrustAgentUnlockDialogHelper carTrustAgentUnlockDialogHelper,
CarStatusBarKeyguardViewManager carStatusBarKeyguardViewManager,
CarStatusBar carStatusBar,
StatusBarStateController statusBarStateController,
@@ -105,21 +66,12 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
mIsUserSwitcherEnabled = resources.getBoolean(R.bool.config_enableFullscreenUserSwitcher);
mMainHandler = mainHandler;
- mUserManager = userManager;
- mCarServiceProvider = carServiceProvider;
- mCarServiceProvider.addListener(
- car -> mEnrollmentManager = (CarTrustAgentEnrollmentManager) car.getCarManager(
- Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE));
-
- mUnlockDialogHelper = carTrustAgentUnlockDialogHelper;
mCarStatusBarKeyguardViewManager = carStatusBarKeyguardViewManager;
mCarStatusBar = carStatusBar;
mStatusBarStateController = statusBarStateController;
mFullScreenUserSwitcherViewController = fullScreenUserSwitcherViewController;
mScreenLifecycle = screenLifecycle;
-
- mCarUserManagerHelper = new CarUserManagerHelper(mContext);
}
@Override
@@ -127,18 +79,6 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
registerUserSwitcherShowListeners();
registerUserSwitcherHideListeners();
registerHideKeyguardListeners();
-
- if (mUserManager.isUserUnlocked(UserHandle.USER_SYSTEM)) {
- // User0 is unlocked, switched to the initial user
- showDialogForInitialUser();
- } else {
- // listen to USER_UNLOCKED
- mContext.registerReceiverAsUser(mUserUnlockReceiver,
- UserHandle.getUserHandleForUid(UserHandle.USER_SYSTEM),
- new IntentFilter(Intent.ACTION_USER_UNLOCKED),
- /* broadcastPermission= */ null,
- /* scheduler= */ null);
- }
}
private void registerUserSwitcherShowListeners() {
@@ -194,26 +134,16 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
}
/**
- * Every time user clicks on an item in the switcher, if the clicked user has no trusted
- * device, we hide the switcher, either gradually or immediately.
- * If the user has trusted device, we show an unlock dialog to notify user the unlock
- * state.
- * When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher.
- * We dismiss the entire keyguard when we hide the switcher if user clicked on the
- * foreground user (user we're already logged in as).
+ * Every time user clicks on an item in the switcher, we hide the switcher.
+ *
+ * We dismiss the entire keyguard if user clicked on the foreground user (user we're already
+ * logged in as).
*/
private void onUserSelected(UserGridRecyclerView.UserRecord record) {
- mSelectedUser = record;
- if (record.mInfo != null) {
- if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) {
- mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener);
- return;
- }
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id);
- }
+ hide();
+ if (record.mType == UserGridRecyclerView.UserRecord.FOREGROUND_USER) {
+ mCarStatusBar.dismissKeyguard();
}
- dismissUserSwitcher();
}
// We automatically dismiss keyguard unless user switcher is being shown above the keyguard.
@@ -234,53 +164,6 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
}
}
- private boolean hasScreenLock(int uid) {
- LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
- return lockPatternUtils.getCredentialTypeForUser(uid)
- != LockPatternUtils.CREDENTIAL_TYPE_NONE;
- }
-
- private boolean hasTrustedDevice(int uid) {
- if (mEnrollmentManager == null) { // car service not ready, so it cannot be available.
- return false;
- }
- return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
- }
-
- private void dismissUserSwitcher() {
- if (mSelectedUser == null) {
- Log.e(TAG, "Request to dismiss user switcher, but no user selected");
- return;
- }
- if (mSelectedUser.mType == UserGridRecyclerView.UserRecord.FOREGROUND_USER) {
- hide();
- mCarStatusBar.dismissKeyguard();
- return;
- }
- hide();
- }
-
- private void showDialogForInitialUser() {
- int initialUser = mCarUserManagerHelper.getInitialUser();
- UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser);
- mSelectedUser = new UserGridRecyclerView.UserRecord(initialUserInfo,
- UserGridRecyclerView.UserRecord.FOREGROUND_USER);
-
- // If the initial user has screen lock and trusted device, display the unlock dialog on the
- // keyguard.
- if (hasScreenLock(initialUser) && hasTrustedDevice(initialUser)) {
- mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
- mOnHideListener);
- } else {
- // If no trusted device, dismiss the keyguard.
- dismissUserSwitcher();
- }
- }
-
- private void invalidateFullscreenUserSwitcherView() {
- mFullScreenUserSwitcherViewController.invalidate();
- }
-
private void hide() {
mFullScreenUserSwitcherViewController.stop();
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java
new file mode 100644
index 000000000000..58022f12e58c
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.window;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import androidx.annotation.CallSuper;
+
+import com.android.systemui.R;
+import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+/**
+ * The {@link OverlayPanelViewController} provides additional dragging animation capabilities to
+ * {@link OverlayViewController}.
+ */
+public abstract class OverlayPanelViewController extends OverlayViewController {
+
+ private static final boolean DEBUG = true;
+ private static final String TAG = "OverlayPanelViewController";
+
+ // used to calculate how fast to open or close the window
+ protected static final float DEFAULT_FLING_VELOCITY = 0;
+ // max time a fling animation takes
+ protected static final float FLING_ANIMATION_MAX_TIME = 0.5f;
+ // acceleration rate for the fling animation
+ protected static final float FLING_SPEED_UP_FACTOR = 0.6f;
+
+ protected static final int SWIPE_DOWN_MIN_DISTANCE = 25;
+ protected static final int SWIPE_MAX_OFF_PATH = 75;
+ protected static final int SWIPE_THRESHOLD_VELOCITY = 200;
+
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private final CarDeviceProvisionedController mCarDeviceProvisionedController;
+ private final View.OnTouchListener mDragOpenTouchListener;
+ private final View.OnTouchListener mDragCloseTouchListener;
+
+ private final int mSettleClosePercentage;
+ private int mPercentageFromBottom;
+
+ private boolean mPanelVisible;
+ private boolean mPanelExpanded;
+
+ private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
+ private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
+
+ private boolean mIsAnimating;
+ private boolean mIsTracking;
+
+ public OverlayPanelViewController(
+ Context context,
+ @Main Resources resources,
+ int stubId,
+ OverlayViewGlobalStateController overlayViewGlobalStateController,
+ FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
+ CarDeviceProvisionedController carDeviceProvisionedController
+ ) {
+ super(stubId, overlayViewGlobalStateController);
+
+ mFlingAnimationUtils = flingAnimationUtilsBuilder
+ .setMaxLengthSeconds(FLING_ANIMATION_MAX_TIME)
+ .setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
+ .build();
+ mCarDeviceProvisionedController = carDeviceProvisionedController;
+
+ mSettleClosePercentage = resources.getInteger(
+ R.integer.notification_settle_close_percentage);
+
+ // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
+ // notification shade.
+ GestureDetector openGestureDetector = new GestureDetector(context,
+ new OpenGestureListener() {
+ @Override
+ protected void open() {
+ animateExpandPanel();
+ }
+ });
+
+ // Attached to the NavBars to close the notification shade
+ GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(context,
+ new SystemBarCloseGestureListener() {
+ @Override
+ protected void close() {
+ if (isPanelExpanded()) {
+ animateCollapsePanel();
+ }
+ }
+ });
+
+ mDragOpenTouchListener = (v, event) -> {
+ if (!mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
+ return true;
+ }
+ if (!isInflated()) {
+ getOverlayViewGlobalStateController().inflateView(this);
+ }
+
+ boolean consumed = openGestureDetector.onTouchEvent(event);
+ if (consumed) {
+ return true;
+ }
+ maybeCompleteAnimation(event);
+ return true;
+ };
+
+ mDragCloseTouchListener = (v, event) -> {
+ if (!isInflated()) {
+ return true;
+ }
+ boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
+ if (consumed) {
+ return true;
+ }
+ maybeCompleteAnimation(event);
+ return true;
+ };
+ }
+
+ /** Toggles the visibility of the panel. */
+ public void toggle() {
+ if (!isInflated()) {
+ getOverlayViewGlobalStateController().inflateView(this);
+ }
+ if (isPanelExpanded()) {
+ animateCollapsePanel();
+ } else {
+ animateExpandPanel();
+ }
+ }
+
+ /* ***************************************************************************************** *
+ * Panel Animation
+ * ***************************************************************************************** */
+
+ /** Animates the closing of the panel. */
+ protected void animateCollapsePanel() {
+ if (!shouldAnimateCollapsePanel()) {
+ return;
+ }
+
+ if (!isPanelExpanded() || !isPanelVisible()) {
+ return;
+ }
+
+ onAnimateCollapsePanel();
+ getOverlayViewGlobalStateController().setWindowFocusable(false);
+ animatePanel(mClosingVelocity, /* isClosing= */ true);
+ }
+
+ /** Determines whether {@link #animateCollapsePanel()} should collapse the panel. */
+ protected abstract boolean shouldAnimateCollapsePanel();
+
+ /** Called when the panel is beginning to collapse. */
+ protected abstract void onAnimateCollapsePanel();
+
+ /** Animates the expansion of the panel. */
+ protected void animateExpandPanel() {
+ if (!shouldAnimateExpandPanel()) {
+ return;
+ }
+
+ if (!mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
+ return;
+ }
+
+ onAnimateExpandPanel();
+ setPanelVisible(true);
+ animatePanel(mOpeningVelocity, /* isClosing= */ false);
+
+ setPanelExpanded(true);
+ }
+
+ /** Determines whether {@link #animateExpandPanel()}} should expand the panel. */
+ protected abstract boolean shouldAnimateExpandPanel();
+
+ /** Called when the panel is beginning to expand. */
+ protected abstract void onAnimateExpandPanel();
+
+ /**
+ * Depending on certain conditions, determines whether to fully expand or collapse the panel.
+ */
+ protected void maybeCompleteAnimation(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP
+ && isPanelVisible()) {
+ if (mSettleClosePercentage < mPercentageFromBottom) {
+ animatePanel(DEFAULT_FLING_VELOCITY, false);
+ } else {
+ animatePanel(DEFAULT_FLING_VELOCITY, true);
+ }
+ }
+ }
+
+ /**
+ * Animates the panel from one position to other. This is used to either open or
+ * close the panel completely with a velocity. If the animation is to close the
+ * panel this method also makes the view invisible after animation ends.
+ */
+ protected void animatePanel(float velocity, boolean isClosing) {
+ float to = 0;
+ if (!isClosing) {
+ to = getLayout().getHeight();
+ }
+
+ Rect rect = getLayout().getClipBounds();
+ if (rect != null && rect.bottom != to) {
+ float from = rect.bottom;
+ animate(from, to, velocity, isClosing);
+ return;
+ }
+
+ // We will only be here if the shade is being opened programmatically or via button when
+ // height of the layout was not calculated.
+ ViewTreeObserver notificationTreeObserver = getLayout().getViewTreeObserver();
+ notificationTreeObserver.addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ ViewTreeObserver obs = getLayout().getViewTreeObserver();
+ obs.removeOnGlobalLayoutListener(this);
+ float to = getLayout().getHeight();
+ animate(/* from= */ 0, to, velocity, isClosing);
+ }
+ });
+ }
+
+ private void animate(float from, float to, float velocity, boolean isClosing) {
+ if (mIsAnimating) {
+ return;
+ }
+ mIsAnimating = true;
+ mIsTracking = true;
+ ValueAnimator animator = ValueAnimator.ofFloat(from, to);
+ animator.addUpdateListener(
+ animation -> {
+ float animatedValue = (Float) animation.getAnimatedValue();
+ setViewClipBounds((int) animatedValue);
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mIsAnimating = false;
+ mIsTracking = false;
+ mOpeningVelocity = DEFAULT_FLING_VELOCITY;
+ mClosingVelocity = DEFAULT_FLING_VELOCITY;
+ if (isClosing) {
+ setPanelVisible(false);
+ getLayout().setClipBounds(null);
+ onCollapseAnimationEnd();
+ setPanelExpanded(false);
+ } else {
+ onExpandAnimationEnd();
+ setPanelExpanded(true);
+ }
+ }
+ });
+ getFlingAnimationUtils().apply(animator, from, to, Math.abs(velocity));
+ animator.start();
+ }
+
+ /**
+ * Called in {@link Animator.AnimatorListener#onAnimationEnd(Animator)} when the panel is
+ * closing.
+ */
+ protected abstract void onCollapseAnimationEnd();
+
+ /**
+ * Called in {@link Animator.AnimatorListener#onAnimationEnd(Animator)} when the panel is
+ * opening.
+ */
+ protected abstract void onExpandAnimationEnd();
+
+ /* ***************************************************************************************** *
+ * Panel Visibility
+ * ***************************************************************************************** */
+
+ /** Set the panel view to be visible. */
+ protected final void setPanelVisible(boolean visible) {
+ mPanelVisible = visible;
+ onPanelVisible(visible);
+ }
+
+ /** Returns {@code true} if panel is visible. */
+ public final boolean isPanelVisible() {
+ return mPanelVisible;
+ }
+
+ /** Business logic run when panel visibility is set. */
+ @CallSuper
+ protected void onPanelVisible(boolean visible) {
+ if (DEBUG) {
+ Log.e(TAG, "onPanelVisible: " + visible);
+ }
+
+ if (visible && !getOverlayViewGlobalStateController().isWindowVisible()) {
+ getOverlayViewGlobalStateController().setWindowVisible(true);
+ }
+ if (!visible && getOverlayViewGlobalStateController().isWindowVisible()) {
+ getOverlayViewGlobalStateController().setWindowVisible(false);
+ }
+ getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ getOverlayViewGlobalStateController().setWindowFocusable(visible);
+ }
+
+ /* ***************************************************************************************** *
+ * Panel Expansion
+ * ***************************************************************************************** */
+
+ /**
+ * Set the panel state to expanded. This will expand or collapse the overlay window if
+ * necessary.
+ */
+ protected final void setPanelExpanded(boolean expand) {
+ mPanelExpanded = expand;
+ onPanelExpanded(expand);
+ }
+
+ /** Returns {@code true} if panel is expanded. */
+ public final boolean isPanelExpanded() {
+ return mPanelExpanded;
+ }
+
+ @CallSuper
+ protected void onPanelExpanded(boolean expand) {
+ if (DEBUG) {
+ Log.e(TAG, "onPanelExpanded: " + expand);
+ }
+ }
+
+ /* ***************************************************************************************** *
+ * Misc
+ * ***************************************************************************************** */
+
+ protected void calculatePercentageFromBottom(float height) {
+ if (getLayout().getHeight() > 0) {
+ mPercentageFromBottom = (int) Math.abs(
+ height / getLayout().getHeight() * 100);
+ }
+ }
+
+ protected void setViewClipBounds(int height) {
+ if (height > getLayout().getHeight()) {
+ height = getLayout().getHeight();
+ }
+ Rect clipBounds = new Rect();
+ clipBounds.set(0, 0, getLayout().getWidth(), height);
+ getLayout().setClipBounds(clipBounds);
+ onScroll(height);
+ }
+
+ /** Called while scrolling. */
+ protected abstract void onScroll(int height);
+
+ /* ***************************************************************************************** *
+ * Getters
+ * ***************************************************************************************** */
+
+ /** Returns the open touch listener. */
+ public final View.OnTouchListener getDragOpenTouchListener() {
+ return mDragOpenTouchListener;
+ }
+
+ /** Returns the close touch listener. */
+ public final View.OnTouchListener getDragCloseTouchListener() {
+ return mDragCloseTouchListener;
+ }
+
+ /** Gets the fling animation utils used for animating this panel. */
+ protected final FlingAnimationUtils getFlingAnimationUtils() {
+ return mFlingAnimationUtils;
+ }
+
+ /** Returns {@code true} if the panel is currently tracking. */
+ protected final boolean isTracking() {
+ return mIsTracking;
+ }
+
+ /** Returns {@code true} if the panel is currently animating. */
+ protected final boolean isAnimating() {
+ return mIsAnimating;
+ }
+
+ /** Returns the percentage of the panel that is open from the bottom. */
+ protected final int getPercentageFromBottom() {
+ return mPercentageFromBottom;
+ }
+
+ /** Returns the percentage at which we've determined whether to open or close the panel. */
+ protected final int getSettleClosePercentage() {
+ return mSettleClosePercentage;
+ }
+
+ /* ***************************************************************************************** *
+ * Gesture Listeners
+ * ***************************************************************************************** */
+
+ /** Called when the user is beginning to scroll down the panel. */
+ protected abstract void onOpenScrollStart();
+
+ /**
+ * Only responsible for open hooks. Since once the panel opens it covers all elements
+ * there is no need to merge with close.
+ */
+ protected abstract class OpenGestureListener extends
+ GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+
+ if (!isPanelVisible()) {
+ onOpenScrollStart();
+ }
+ setPanelVisible(true);
+
+ // clips the view for the notification shade when the user scrolls to open.
+ setViewClipBounds((int) event2.getRawY());
+
+ // Initially the scroll starts with height being zero. This checks protects from divide
+ // by zero error.
+ calculatePercentageFromBottom(event2.getRawY());
+
+ mIsTracking = true;
+ return true;
+ }
+
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ if (velocityY > SWIPE_THRESHOLD_VELOCITY) {
+ mOpeningVelocity = velocityY;
+ open();
+ return true;
+ }
+ animatePanel(DEFAULT_FLING_VELOCITY, true);
+
+ return false;
+ }
+
+ protected abstract void open();
+ }
+
+ /** Determines whether the scroll event should allow closing of the panel. */
+ protected abstract boolean shouldAllowClosingScroll();
+
+ protected abstract class CloseGestureListener extends
+ GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent motionEvent) {
+ if (isPanelExpanded()) {
+ animatePanel(DEFAULT_FLING_VELOCITY, true);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+ // should not clip while scroll to the bottom of the list.
+ if (!shouldAllowClosingScroll()) {
+ return false;
+ }
+ float actualNotificationHeight =
+ getLayout().getHeight() - (event1.getRawY() - event2.getRawY());
+ if (actualNotificationHeight > getLayout().getHeight()) {
+ actualNotificationHeight = getLayout().getHeight();
+ }
+ if (getLayout().getHeight() > 0) {
+ mPercentageFromBottom = (int) Math.abs(
+ actualNotificationHeight / getLayout().getHeight() * 100);
+ boolean isUp = distanceY > 0;
+
+ // This check is to figure out if onScroll was called while swiping the card at
+ // bottom of the list. At that time we should not allow notification shade to
+ // close. We are also checking for the upwards swipe gesture here because it is
+ // possible if a user is closing the notification shade and while swiping starts
+ // to open again but does not fling. At that time we should allow the
+ // notification shade to close fully or else it would stuck in between.
+ if (Math.abs(getLayout().getHeight() - actualNotificationHeight)
+ > SWIPE_DOWN_MIN_DISTANCE && isUp) {
+ setViewClipBounds((int) actualNotificationHeight);
+ mIsTracking = true;
+ } else if (!isUp) {
+ setViewClipBounds((int) actualNotificationHeight);
+ }
+ }
+ // if we return true the items in RV won't be scrollable.
+ return false;
+ }
+
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ // should not fling if the touch does not start when view is at the bottom of the list.
+ if (!shouldAllowClosingScroll()) {
+ return false;
+ }
+ if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
+ || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
+ // swipe was not vertical or was not fast enough
+ return false;
+ }
+ boolean isUp = velocityY < 0;
+ if (isUp) {
+ close();
+ return true;
+ } else {
+ // we should close the shade
+ animatePanel(velocityY, false);
+ }
+ return false;
+ }
+
+ protected abstract void close();
+ }
+
+ protected abstract class SystemBarCloseGestureListener extends CloseGestureListener {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ mClosingVelocity = DEFAULT_FLING_VELOCITY;
+ if (isPanelExpanded()) {
+ close();
+ }
+ return super.onSingleTapUp(e);
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+ calculatePercentageFromBottom(event2.getRawY());
+ setViewClipBounds((int) event2.getRawY());
+ return true;
+ }
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
index f2748b89c95c..76557fda6926 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
@@ -20,13 +20,14 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Handler;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
import android.view.LayoutInflater;
import android.view.WindowManager;
+import androidx.test.filters.SmallTest;
+
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java
new file mode 100644
index 000000000000..04f2d06ca71c
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.window;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.tests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class OverlayPanelViewControllerTest extends SysuiTestCase {
+ private TestOverlayPanelViewController mOverlayPanelViewController;
+ private ViewGroup mBaseLayout;
+
+ @Mock
+ private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
+ @Mock
+ private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
+ @Mock
+ private FlingAnimationUtils mFlingAnimationUtils;
+ @Mock
+ private CarDeviceProvisionedController mCarDeviceProvisionedController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.overlay_view_controller_test, /* root= */ null);
+
+ when(mFlingAnimationUtilsBuilder.setMaxLengthSeconds(anyFloat())).thenReturn(
+ mFlingAnimationUtilsBuilder);
+ when(mFlingAnimationUtilsBuilder.setSpeedUpFactor(anyFloat())).thenReturn(
+ mFlingAnimationUtilsBuilder);
+ when(mFlingAnimationUtilsBuilder.build()).thenReturn(mFlingAnimationUtils);
+ mOverlayPanelViewController = new TestOverlayPanelViewController(
+ getContext(),
+ getContext().getOrCreateTestableResources().getResources(),
+ R.id.overlay_view_controller_stub,
+ mOverlayViewGlobalStateController,
+ mFlingAnimationUtilsBuilder,
+ mCarDeviceProvisionedController);
+ }
+
+ @Test
+ public void toggle_notInflated_inflates() {
+ assertThat(mOverlayPanelViewController.isInflated()).isFalse();
+
+ mOverlayPanelViewController.toggle();
+
+ verify(mOverlayViewGlobalStateController).inflateView(mOverlayPanelViewController);
+ }
+
+ @Test
+ public void toggle_inflated_doesNotInflate() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ assertThat(mOverlayPanelViewController.isInflated()).isTrue();
+
+ mOverlayPanelViewController.toggle();
+
+ verify(mOverlayViewGlobalStateController, never()).inflateView(mOverlayPanelViewController);
+ }
+
+ @Test
+ public void toggle_notExpanded_panelExpands() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setPanelExpanded(false);
+
+ mOverlayPanelViewController.toggle();
+
+ assertThat(mOverlayPanelViewController.mAnimateExpandPanelCalled).isTrue();
+ }
+
+ @Test
+ public void toggle_expanded_panelCollapses() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setPanelExpanded(true);
+
+ mOverlayPanelViewController.toggle();
+
+ assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue();
+ }
+
+ @Test
+ public void animateCollapsePanel_shouldNotAnimateCollapsePanel_doesNotCollapse() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateCollapsePanel(false);
+
+ mOverlayPanelViewController.animateCollapsePanel();
+
+ assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue();
+ assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isFalse();
+ }
+
+ @Test
+ public void animateCollapsePanel_isNotExpanded_doesNotCollapse() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateCollapsePanel(true);
+ mOverlayPanelViewController.setPanelExpanded(false);
+
+ mOverlayPanelViewController.animateCollapsePanel();
+
+ assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue();
+ assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isFalse();
+ }
+
+ @Test
+ public void animateCollapsePanel_isNotVisible_doesNotCollapse() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateCollapsePanel(true);
+ mOverlayPanelViewController.setPanelExpanded(true);
+ mOverlayPanelViewController.setPanelVisible(false);
+
+ mOverlayPanelViewController.animateCollapsePanel();
+
+ assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue();
+ assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isFalse();
+ }
+
+ @Test
+ public void animateCollapsePanel_collapses() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateCollapsePanel(true);
+ mOverlayPanelViewController.setPanelExpanded(true);
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ mOverlayPanelViewController.animateCollapsePanel();
+
+ assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isTrue();
+ }
+
+ @Test
+ public void animateCollapsePanel_removesWindowFocus() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateCollapsePanel(true);
+ mOverlayPanelViewController.setPanelExpanded(true);
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ mOverlayPanelViewController.animateCollapsePanel();
+
+ verify(mOverlayViewGlobalStateController).setWindowFocusable(false);
+ }
+
+ @Test
+ public void animateExpandPanel_shouldNotAnimateExpandPanel_doesNotExpand() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateExpandPanel(false);
+
+ mOverlayPanelViewController.animateExpandPanel();
+
+ assertThat(mOverlayPanelViewController.mAnimateExpandPanelCalled).isTrue();
+ assertThat(mOverlayPanelViewController.mOnAnimateExpandPanelCalled).isFalse();
+ }
+
+ @Test
+ public void animateExpandPanel_userNotSetup_doesNotExpand() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateExpandPanel(true);
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(false);
+
+ mOverlayPanelViewController.animateExpandPanel();
+
+ assertThat(mOverlayPanelViewController.mAnimateExpandPanelCalled).isTrue();
+ assertThat(mOverlayPanelViewController.mOnAnimateExpandPanelCalled).isFalse();
+ }
+
+ @Test
+ public void animateExpandPanel_expands() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateExpandPanel(true);
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true);
+
+ mOverlayPanelViewController.animateExpandPanel();
+
+ assertThat(mOverlayPanelViewController.mOnAnimateExpandPanelCalled).isTrue();
+ }
+
+ @Test
+ public void animateExpandPanel_setsPanelVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateExpandPanel(true);
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true);
+
+ mOverlayPanelViewController.animateExpandPanel();
+
+ assertThat(mOverlayPanelViewController.isPanelVisible()).isTrue();
+ }
+
+ @Test
+ public void animateExpandPanel_setsPanelExpanded() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateExpandPanel(true);
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true);
+
+ mOverlayPanelViewController.animateExpandPanel();
+
+ assertThat(mOverlayPanelViewController.isPanelExpanded()).isTrue();
+ }
+
+ @Test
+ public void setPanelVisible_setTrue_windowNotVisible_setsWindowVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(false);
+
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ verify(mOverlayViewGlobalStateController).setWindowVisible(true);
+ }
+
+ @Test
+ public void setPanelVisible_setTrue_windowVisible_doesNotSetWindowVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(true);
+
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ verify(mOverlayViewGlobalStateController, never()).setWindowVisible(true);
+ }
+
+ @Test
+ public void setPanelVisible_setTrue_setLayoutVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.getLayout().setVisibility(View.INVISIBLE);
+
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ assertThat(mOverlayPanelViewController.getLayout().getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void setPanelVisible_setTrue_setWindowFocusable() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ verify(mOverlayViewGlobalStateController).setWindowFocusable(true);
+ }
+
+ @Test
+ public void setPanelVisible_setFalse_windowVisible_setsWindowNotVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(true);
+
+ mOverlayPanelViewController.setPanelVisible(false);
+
+ verify(mOverlayViewGlobalStateController).setWindowVisible(false);
+ }
+
+ @Test
+ public void setPanelVisible_setFalse_windowNotVisible_doesNotSetWindowNotVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(false);
+
+ mOverlayPanelViewController.setPanelVisible(false);
+
+ verify(mOverlayViewGlobalStateController, never()).setWindowVisible(false);
+ }
+
+ @Test
+ public void setPanelVisible_setFalse_setLayoutInvisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.getLayout().setVisibility(View.VISIBLE);
+
+ mOverlayPanelViewController.setPanelVisible(false);
+
+ assertThat(mOverlayPanelViewController.getLayout().getVisibility()).isEqualTo(
+ View.INVISIBLE);
+ }
+
+ @Test
+ public void setPanelVisible_setFalse_setWindowNotFocusable() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+
+ mOverlayPanelViewController.setPanelVisible(false);
+
+ verify(mOverlayViewGlobalStateController).setWindowFocusable(false);
+ }
+
+ @Test
+ public void dragOpenTouchListener_isNotInflated_inflatesView() {
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true);
+ assertThat(mOverlayPanelViewController.isInflated()).isFalse();
+
+ mOverlayPanelViewController.getDragOpenTouchListener().onTouch(/* v= */ null,
+ MotionEvent.obtain(/* downTime= */ 200, /* eventTime= */ 300,
+ MotionEvent.ACTION_MOVE, /* x= */ 0, /* y= */ 0, /* metaState= */ 0));
+
+ verify(mOverlayViewGlobalStateController).inflateView(mOverlayPanelViewController);
+ }
+
+ private static class TestOverlayPanelViewController extends OverlayPanelViewController {
+
+ private boolean mShouldAnimateCollapsePanel;
+ private boolean mShouldAnimateExpandPanel;
+ private boolean mShouldAllowClosingScroll;
+
+ boolean mOnAnimateCollapsePanelCalled;
+ boolean mAnimateCollapsePanelCalled;
+ boolean mOnAnimateExpandPanelCalled;
+ boolean mAnimateExpandPanelCalled;
+ boolean mOnCollapseAnimationEndCalled;
+ boolean mOnExpandAnimationEndCalled;
+ boolean mOnOpenScrollStartEnd;
+ List<Integer> mOnScrollHeights;
+
+ TestOverlayPanelViewController(
+ Context context,
+ Resources resources,
+ int stubId,
+ OverlayViewGlobalStateController overlayViewGlobalStateController,
+ FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
+ CarDeviceProvisionedController carDeviceProvisionedController) {
+ super(context, resources, stubId, overlayViewGlobalStateController,
+ flingAnimationUtilsBuilder,
+ carDeviceProvisionedController);
+
+ mOnScrollHeights = new ArrayList<>();
+ }
+
+ public void setShouldAnimateCollapsePanel(boolean shouldAnimate) {
+ mShouldAnimateCollapsePanel = shouldAnimate;
+ }
+
+ @Override
+ protected boolean shouldAnimateCollapsePanel() {
+ return mShouldAnimateCollapsePanel;
+ }
+
+ @Override
+ protected void animateCollapsePanel() {
+ super.animateCollapsePanel();
+ mAnimateCollapsePanelCalled = true;
+ }
+
+ @Override
+ protected void onAnimateCollapsePanel() {
+ mOnAnimateCollapsePanelCalled = true;
+ }
+
+ public void setShouldAnimateExpandPanel(boolean shouldAnimate) {
+ mShouldAnimateExpandPanel = shouldAnimate;
+ }
+
+ @Override
+ protected boolean shouldAnimateExpandPanel() {
+ return mShouldAnimateExpandPanel;
+ }
+
+ @Override
+ protected void animateExpandPanel() {
+ super.animateExpandPanel();
+ mAnimateExpandPanelCalled = true;
+ }
+
+ @Override
+ protected void onAnimateExpandPanel() {
+ mOnAnimateExpandPanelCalled = true;
+ }
+
+ @Override
+ protected void onCollapseAnimationEnd() {
+ mOnCollapseAnimationEndCalled = true;
+ }
+
+ @Override
+ protected void onExpandAnimationEnd() {
+ mOnExpandAnimationEndCalled = true;
+ }
+
+ @Override
+ protected void onScroll(int height) {
+ mOnScrollHeights.add(height);
+ }
+
+ @Override
+ protected void onOpenScrollStart() {
+ mOnOpenScrollStartEnd = true;
+ }
+
+ public void setShouldAllowClosingScroll(boolean shouldAllow) {
+ mShouldAllowClosingScroll = shouldAllow;
+ }
+
+ @Override
+ protected boolean shouldAllowClosingScroll() {
+ return mShouldAllowClosingScroll;
+ }
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java
index 3be962627f62..331326168ba4 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java
@@ -43,7 +43,7 @@ import org.mockito.MockitoAnnotations;
@TestableLooper.RunWithLooper
@SmallTest
public class OverlayViewControllerTest extends SysuiTestCase {
- private MockOverlayViewController mOverlayViewController;
+ private TestOverlayViewController mOverlayViewController;
private ViewGroup mBaseLayout;
@Mock
@@ -56,7 +56,7 @@ public class OverlayViewControllerTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(/* testClass= */ this);
- mOverlayViewController = new MockOverlayViewController(R.id.overlay_view_controller_stub,
+ mOverlayViewController = new TestOverlayViewController(R.id.overlay_view_controller_stub,
mOverlayViewGlobalStateController);
mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
@@ -130,12 +130,12 @@ public class OverlayViewControllerTest extends SysuiTestCase {
assertThat(mOverlayViewController.mHideInternalCalled).isFalse();
}
- private static class MockOverlayViewController extends OverlayViewController {
+ private static class TestOverlayViewController extends OverlayViewController {
boolean mOnFinishInflateCalled = false;
boolean mShowInternalCalled = false;
boolean mHideInternalCalled = false;
- MockOverlayViewController(int stubId,
+ TestOverlayViewController(int stubId,
OverlayViewGlobalStateController overlayViewGlobalStateController) {
super(stubId, overlayViewGlobalStateController);
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 53e7921879d1..1007d8379b8e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -646,6 +646,8 @@
<string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string>
<!-- Setting Checkbox title whether to disable WiFi Scan Throttling. [CHAR LIMIT=40] -->
<string name="wifi_scan_throttling">Wi\u2011Fi scan throttling</string>
+ <!-- Setting Checkbox title whether to enable WiFi enhanced mac randomization. [CHAR LIMIT=40] -->
+ <string name="wifi_enhanced_mac_randomization">Wi\u2011Fi\u2011enhanced MAC randomization</string>
<!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] -->
<string name="mobile_data_always_on">Mobile data always active</string>
<!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] -->
@@ -716,6 +718,8 @@
<string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string>
<!-- Setting Checkbox summary whether to disable Wifi scan throttling [CHAR LIMIT=NONE] -->
<string name="wifi_scan_throttling_summary">Reduces battery drain &amp; improves network performance</string>
+ <!-- Setting Checkbox title whether to enable WiFi enhanced mac randomization. [CHAR LIMIT=NONE] -->
+ <string name="wifi_enhanced_mac_randomization_summary">This toggle affects MAC randomization behavior for client mode only.\nWhen this mode is activated, any networks that have MAC randomization enabled may have their MAC addresses re\u2011randomized during association, depending on when the client last disconnected from the network. Re\u2011randomization does not occur if the device reconnects in 4 hours or less.</string>
<!-- Label indicating network has been manually marked as metered -->
<string name="wifi_metered_label">Metered</string>
<!-- Label indicating network has been manually marked as unmetered -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index 8aa0aec28fb8..a53bc9f966d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -102,12 +102,11 @@ public class WifiEntryPreference extends Preference implements WifiEntry.WifiEnt
// Turn off divider
view.findViewById(R.id.two_target_divider).setVisibility(View.INVISIBLE);
- // Enable the icon button when this Entry is a canManageSubscription entry.
+ // Enable the icon button when the help string in this WifiEntry is not null.
final ImageButton imageButton = (ImageButton) view.findViewById(R.id.icon_button);
final ImageView frictionImageView = (ImageView) view.findViewById(
R.id.friction_icon);
- if (mWifiEntry.canManageSubscription() && !mWifiEntry.isSaved()
- && !mWifiEntry.isSubscription()
+ if (mWifiEntry.getHelpUriString() != null
&& mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) {
final Drawable drawablehelp = getDrawable(R.drawable.ic_help);
drawablehelp.setTintList(
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
index a9f31ce12b42..46e699d3bed5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
@@ -64,7 +64,7 @@ public class WifiEntryPreferenceTest {
private static final String MOCK_TITLE = "title";
private static final String MOCK_SUMMARY = "summary";
-
+ private static final String FAKE_URI_STRING = "fakeuri";
@Before
public void setUp() {
@@ -155,22 +155,23 @@ public class WifiEntryPreferenceTest {
}
@Test
- public void canManageSubscription_shouldSetImageButtonVisible() {
- when(mMockWifiEntry.canManageSubscription()).thenReturn(true);
+ public void notNull_whenGetHelpUriString_shouldSetImageButtonVisible() {
+ when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
final WifiEntryPreference pref =
new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
final LayoutInflater inflater = LayoutInflater.from(mContext);
final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
false);
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
+
pref.onBindViewHolder(holder);
assertThat(view.findViewById(R.id.icon_button).getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
- public void helpButton_whenCanManageSubscription_shouldSetCorrectContentDescription() {
- when(mMockWifiEntry.canManageSubscription()).thenReturn(true);
+ public void helpButton_whenGetHelpUriStringNotNull_shouldSetCorrectContentDescription() {
+ when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
final WifiEntryPreference pref =
new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
final LayoutInflater inflater = LayoutInflater.from(mContext);
diff --git a/packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml b/packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml
index b18c3e77ef10..021c3eee50df 100644
--- a/packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml
+++ b/packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml
@@ -20,15 +20,15 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:pathData="M16,19H5V7C5.0016,6.47 5.2128,5.9623 5.5875,5.5875C5.9623,5.2128 6.47,5.0016 7,5H14C14.5299,5.0016 15.0377,5.2128 15.4125,5.5875C15.7872,5.9623 15.9984,6.47 16,7V8H18V7C18,5.9391 17.5786,4.9217 16.8284,4.1716C16.0783,3.4214 15.0609,3 14,3H7C5.9391,3 4.9217,3.4214 4.1716,4.1716C3.4214,4.9217 3,5.9391 3,7V21H18V17H16V19Z"
+ android:pathData="M10,3H6C5.2043,3 4.4413,3.3161 3.8787,3.8787C3.3161,4.4413 3,5.2043 3,6V21H13V6C13,5.2043 12.6839,4.4413 12.1213,3.8787C11.5587,3.3161 10.7956,3 10,3ZM11,15H10V10H11V15ZM10,8C9.4696,8 8.9609,8.2107 8.5858,8.5858C8.2107,8.9609 8,9.4696 8,10V15C8,15.5304 8.2107,16.0391 8.5858,16.4142C8.9609,16.7893 9.4696,17 10,17H11V19H5V6C5,5.7348 5.1054,5.4804 5.2929,5.2929C5.4804,5.1054 5.7348,5 6,5H10C10.2652,5 10.5196,5.1054 10.7071,5.2929C10.8946,5.4804 11,5.7348 11,6V8H10Z"
android:fillColor="#FF000000" />
<path
- android:pathData="M19,13C18.6049,13.378 18.1309,13.6638 17.6122,13.8367C17.0935,14.0096 16.5429,14.0653 16,14V16C16.5429,16.0653 17.0935,16.0096 17.6122,15.8367C18.1309,15.6638 18.6049,15.378 19,15C19.93,14.02 20,14 21,14V12C20,12 19.93,12.02 19,13Z"
+ android:pathData="M18.445,11.168C17.398,11.868 16.574,11.468 15.316,11.051L14.684,12.951C15.476,13.3014 16.3252,13.5047 17.19,13.551C18.0324,13.5503 18.8555,13.2994 19.555,12.83C20.596,12.135 21.383,12.514 22.684,12.947L23.316,11.047C22.688,10.842 20.57,9.753 18.445,11.168Z"
android:fillColor="#FF000000" />
<path
- android:pathData="M19,9C18.6049,9.378 18.1309,9.6638 17.6122,9.8367C17.0935,10.0096 16.5429,10.0653 16,10V12C16.5429,12.0653 17.0935,12.0096 17.6122,11.8367C18.1309,11.6638 18.6049,11.378 19,11C19.93,10.02 20,10 21,10V8C20,8 19.93,8.02 19,9Z"
+ android:pathData="M17.189,9.659C18.0316,9.658 18.855,9.4071 19.555,8.938C20.596,8.244 21.384,8.622 22.684,9.055L23.316,7.155C22.727,6.955 20.583,5.8489 18.445,7.2719C17.406,7.966 16.615,7.59 15.317,7.156L14.683,9.056C15.4748,9.4074 16.324,9.6117 17.189,9.659V9.659Z"
android:fillColor="#FF000000" />
<path
- android:pathData="M10.5,8C9.7089,8 8.9355,8.2346 8.2777,8.6741C7.6199,9.1137 7.1072,9.7383 6.8045,10.4692C6.5017,11.2001 6.4225,12.0044 6.5769,12.7803C6.7312,13.5563 7.1122,14.269 7.6716,14.8284C8.231,15.3878 8.9437,15.7688 9.7196,15.9232C10.4956,16.0775 11.2998,15.9982 12.0307,15.6955C12.7616,15.3927 13.3864,14.8801 13.8259,14.2223C14.2654,13.5645 14.5,12.7911 14.5,12C14.5,10.9391 14.0786,9.9217 13.3284,9.1716C12.5783,8.4214 11.5609,8 10.5,8ZM10.5,14C10.1044,14 9.7178,13.8827 9.3889,13.663C9.06,13.4432 8.8036,13.1308 8.6522,12.7654C8.5009,12.3999 8.4613,11.9978 8.5384,11.6098C8.6156,11.2218 8.8061,10.8655 9.0858,10.5858C9.3655,10.3061 9.7219,10.1156 10.1098,10.0385C10.4978,9.9613 10.8999,10.0008 11.2654,10.1522C11.6308,10.3036 11.9432,10.56 12.1629,10.8889C12.3827,11.2178 12.5,11.6044 12.5,12C12.5,12.5304 12.2893,13.0391 11.9142,13.4142C11.5391,13.7893 11.0304,14 10.5,14Z"
+ android:pathData="M18.445,15.063C17.41,15.752 16.634,15.384 15.316,14.945L14.684,16.845C15.4762,17.1948 16.3253,17.3981 17.19,17.445C18.0322,17.4438 18.8551,17.1932 19.555,16.725C20.594,16.031 21.385,16.407 22.683,16.841L23.317,14.941C22.728,14.749 20.583,13.637 18.445,15.063Z"
android:fillColor="#FF000000" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml b/packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml
index b18c3e77ef10..9533cfe30b75 100644
--- a/packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml
+++ b/packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml
@@ -20,15 +20,18 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:pathData="M16,19H5V7C5.0016,6.47 5.2128,5.9623 5.5875,5.5875C5.9623,5.2128 6.47,5.0016 7,5H14C14.5299,5.0016 15.0377,5.2128 15.4125,5.5875C15.7872,5.9623 15.9984,6.47 16,7V8H18V7C18,5.9391 17.5786,4.9217 16.8284,4.1716C16.0783,3.4214 15.0609,3 14,3H7C5.9391,3 4.9217,3.4214 4.1716,4.1716C3.4214,4.9217 3,5.9391 3,7V21H18V17H16V19Z"
+ android:pathData="M10,3H6C5.2043,3 4.4413,3.3161 3.8787,3.8787C3.3161,4.4413 3,5.2043 3,6V21H13V17H10C9.4696,17 8.9609,16.7893 8.5858,16.4142C8.2107,16.0391 8,15.5304 8,15V10C8,9.4696 8.2107,8.9609 8.5858,8.5858C8.9609,8.2107 9.4696,8 10,8H13V6C13,5.2043 12.6839,4.4413 12.1213,3.8787C11.5587,3.3161 10.7956,3 10,3Z"
android:fillColor="#FF000000" />
<path
- android:pathData="M19,13C18.6049,13.378 18.1309,13.6638 17.6122,13.8367C17.0935,14.0096 16.5429,14.0653 16,14V16C16.5429,16.0653 17.0935,16.0096 17.6122,15.8367C18.1309,15.6638 18.6049,15.378 19,15C19.93,14.02 20,14 21,14V12C20,12 19.93,12.02 19,13Z"
+ android:pathData="M13,10H10V15H13V10Z"
android:fillColor="#FF000000" />
<path
- android:pathData="M19,9C18.6049,9.378 18.1309,9.6638 17.6122,9.8367C17.0935,10.0096 16.5429,10.0653 16,10V12C16.5429,12.0653 17.0935,12.0096 17.6122,11.8367C18.1309,11.6638 18.6049,11.378 19,11C19.93,10.02 20,10 21,10V8C20,8 19.93,8.02 19,9Z"
+ android:pathData="M18.445,11.168C17.398,11.868 16.574,11.468 15.316,11.051L14.684,12.951C15.476,13.3014 16.3252,13.5047 17.19,13.551C18.0324,13.5503 18.8555,13.2994 19.555,12.83C20.596,12.135 21.383,12.514 22.684,12.947L23.316,11.047C22.688,10.842 20.57,9.753 18.445,11.168Z"
android:fillColor="#FF000000" />
<path
- android:pathData="M10.5,8C9.7089,8 8.9355,8.2346 8.2777,8.6741C7.6199,9.1137 7.1072,9.7383 6.8045,10.4692C6.5017,11.2001 6.4225,12.0044 6.5769,12.7803C6.7312,13.5563 7.1122,14.269 7.6716,14.8284C8.231,15.3878 8.9437,15.7688 9.7196,15.9232C10.4956,16.0775 11.2998,15.9982 12.0307,15.6955C12.7616,15.3927 13.3864,14.8801 13.8259,14.2223C14.2654,13.5645 14.5,12.7911 14.5,12C14.5,10.9391 14.0786,9.9217 13.3284,9.1716C12.5783,8.4214 11.5609,8 10.5,8ZM10.5,14C10.1044,14 9.7178,13.8827 9.3889,13.663C9.06,13.4432 8.8036,13.1308 8.6522,12.7654C8.5009,12.3999 8.4613,11.9978 8.5384,11.6098C8.6156,11.2218 8.8061,10.8655 9.0858,10.5858C9.3655,10.3061 9.7219,10.1156 10.1098,10.0385C10.4978,9.9613 10.8999,10.0008 11.2654,10.1522C11.6308,10.3036 11.9432,10.56 12.1629,10.8889C12.3827,11.2178 12.5,11.6044 12.5,12C12.5,12.5304 12.2893,13.0391 11.9142,13.4142C11.5391,13.7893 11.0304,14 10.5,14Z"
+ android:pathData="M17.189,9.659C18.0316,9.658 18.855,9.4071 19.555,8.938C20.596,8.244 21.384,8.622 22.684,9.055L23.316,7.155C22.727,6.955 20.583,5.8489 18.445,7.2719C17.406,7.966 16.615,7.59 15.317,7.156L14.683,9.056C15.4748,9.4074 16.324,9.6117 17.189,9.659V9.659Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M18.445,15.063C17.41,15.752 16.634,15.384 15.316,14.945L14.684,16.845C15.4762,17.1948 16.3253,17.3981 17.19,17.445C18.0322,17.4438 18.8551,17.1932 19.555,16.725C20.594,16.031 21.385,16.407 22.683,16.841L23.317,14.941C22.728,14.749 20.583,13.637 18.445,15.063Z"
android:fillColor="#FF000000" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_cooking_off.xml b/packages/SystemUI/res/drawable/ic_device_cooking_off.xml
new file mode 100644
index 000000000000..272a3bb79d40
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_cooking_off.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M9,16H2V18H9V21H11V18C11,17.4696 10.7893,16.9609 10.4142,16.5858C10.0391,16.2107 9.5304,16 9,16Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M15,16C14.4696,16 13.9609,16.2107 13.5858,16.5858C13.2107,16.9609 13,17.4696 13,18V21H15V18H22V16H15Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M6,15H18C18.7956,15 19.5587,14.6839 20.1213,14.1213C20.6839,13.5587 21,12.7956 21,12V8H3V12C3,12.7956 3.3161,13.5587 3.8787,14.1213C4.4413,14.6839 5.2043,15 6,15ZM5,10H19V12C19,12.2652 18.8946,12.5196 18.7071,12.7072C18.5196,12.8947 18.2652,13 18,13H6C5.7348,13 5.4804,12.8947 5.2929,12.7072C5.1054,12.5196 5,12.2652 5,12V10Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M21,5H15V4C15,3.7348 14.8946,3.4804 14.7071,3.2929C14.5196,3.1053 14.2652,3 14,3H10C9.7348,3 9.4804,3.1053 9.2929,3.2929C9.1054,3.4804 9,3.7348 9,4V5H3V7H21V5Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_cooking_on.xml b/packages/SystemUI/res/drawable/ic_device_cooking_on.xml
new file mode 100644
index 000000000000..3785f8bffc81
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_cooking_on.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M9,16H2V18H9V21H11V18C11,17.4696 10.7893,16.9609 10.4142,16.5858C10.0391,16.2107 9.5304,16 9,16Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M13,18V21H15V18H22V16H15C14.4696,16 13.9609,16.2107 13.5858,16.5858C13.2107,16.9609 13,17.4696 13,18Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M3,12C3,12.7956 3.3161,13.5587 3.8787,14.1213C4.4413,14.6839 5.2043,15 6,15H18C18.7956,15 19.5587,14.6839 20.1213,14.1213C20.6839,13.5587 21,12.7956 21,12V8H3V12Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M21,5H15V4C15,3.7348 14.8946,3.4804 14.7071,3.2929C14.5196,3.1053 14.2652,3 14,3H10C9.7348,3 9.4804,3.1053 9.2929,3.2929C9.1054,3.4804 9,3.7348 9,4V5H3V7H21V5Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_display_off.xml b/packages/SystemUI/res/drawable/ic_device_display_off.xml
new file mode 100644
index 000000000000..07737c959339
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_display_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M20,3H4C3.4701,3.0016 2.9623,3.2129 2.5875,3.5877C2.2128,3.9624 2.0016,4.47 2,5V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19H8V21H16V19H20C20.5287,18.9974 21.0348,18.7854 21.4078,18.4106C21.7807,18.0359 21.99,17.5287 21.99,17L22,5C21.9984,4.47 21.7872,3.9624 21.4125,3.5877C21.0377,3.2129 20.5299,3.0016 20,3V3ZM20,17H4V5H20V17Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_display_on.xml b/packages/SystemUI/res/drawable/ic_device_display_on.xml
new file mode 100644
index 000000000000..2416f6bb4b83
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_display_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M20,3H4C3.4701,3.0016 2.9623,3.2129 2.5875,3.5877C2.2128,3.9624 2.0016,4.47 2,5V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19H8V21H16V19H20C20.5287,18.9974 21.0348,18.7854 21.4078,18.4106C21.7807,18.0359 21.99,17.5287 21.99,17L22,5C21.9984,4.47 21.7872,3.9624 21.4125,3.5877C21.0377,3.2129 20.5299,3.0016 20,3V3ZM20,17H4V5H20V17Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M19,6H5V16H19V6Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_door_off.xml b/packages/SystemUI/res/drawable/ic_device_door_off.xml
new file mode 100644
index 000000000000..291f312e18a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_door_off.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M7,14H5C4.7348,14 4.4804,14.1054 4.2929,14.293C4.1054,14.4805 4,14.7348 4,15C4,15.2652 4.1054,15.5195 4.2929,15.707C4.4804,15.8946 4.7348,16 5,16H7V21H17V3H7V14ZM15,5V19H9V16H12C12.2652,16 12.5196,15.8946 12.7071,15.707C12.8946,15.5195 13,15.2652 13,15C13,14.7348 12.8946,14.4805 12.7071,14.293C12.5196,14.1054 12.2652,14 12,14H9V5H15Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M20,3C19.7348,3 19.4804,3.1054 19.2929,3.293C19.1054,3.4805 19,3.7348 19,4V8C19,8.2652 19.1054,8.5195 19.2929,8.707C19.4804,8.8946 19.7348,9 20,9C20.2652,9 20.5196,8.8946 20.7071,8.707C20.8946,8.5195 21,8.2652 21,8V4C21,3.7348 20.8946,3.4805 20.7071,3.293C20.5196,3.1054 20.2652,3 20,3Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M12,10C12.5523,10 13,9.5523 13,9C13,8.4477 12.5523,8 12,8C11.4477,8 11,8.4477 11,9C11,9.5523 11.4477,10 12,10Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_door_on.xml b/packages/SystemUI/res/drawable/ic_device_door_on.xml
new file mode 100644
index 000000000000..e6cdf11f8dea
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_door_on.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M20,3C19.7348,3 19.4804,3.1054 19.2929,3.293C19.1054,3.4805 19,3.7348 19,4V8C19,8.2652 19.1054,8.5195 19.2929,8.707C19.4804,8.8946 19.7348,9 20,9C20.2652,9 20.5196,8.8946 20.7071,8.707C20.8946,8.5195 21,8.2652 21,8V4C21,3.7348 20.8946,3.4805 20.7071,3.293C20.5196,3.1054 20.2652,3 20,3Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M13,15C13,14.7348 12.8946,14.4805 12.7071,14.293C12.5196,14.1054 12.2652,14 12,14H5C4.7348,14 4.4804,14.1054 4.2929,14.293C4.1054,14.4805 4,14.7348 4,15C4,15.2652 4.1054,15.5195 4.2929,15.707C4.4804,15.8946 4.7348,16 5,16H12C12.2652,16 12.5196,15.8946 12.7071,15.707C12.8946,15.5195 13,15.2652 13,15Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M7,13H12C12.5304,13 13.0391,13.2109 13.4142,13.5859C13.7893,13.961 14,14.4696 14,15C14,15.5304 13.7893,16.039 13.4142,16.4141C13.0391,16.7891 12.5304,17 12,17H7V21H17V3H7V13ZM12,8C12.1978,8 12.3911,8.0586 12.5556,8.1685C12.72,8.2783 12.8482,8.4345 12.9239,8.6172C12.9996,8.7999 13.0194,9.0013 12.9808,9.1953C12.9422,9.3893 12.847,9.5672 12.7071,9.707C12.5673,9.8469 12.3891,9.9424 12.1951,9.981C12.0011,10.0195 11.8,9.9995 11.6173,9.9238C11.4346,9.8481 11.2784,9.7201 11.1685,9.5557C11.0586,9.3912 11,9.1978 11,9C11,8.7348 11.1054,8.4805 11.2929,8.293C11.4804,8.1054 11.7348,8 12,8Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_garage_on.xml b/packages/SystemUI/res/drawable/ic_device_garage_on.xml
index 8865983f0d7d..eeb4bc153c5d 100644
--- a/packages/SystemUI/res/drawable/ic_device_garage_on.xml
+++ b/packages/SystemUI/res/drawable/ic_device_garage_on.xml
@@ -20,9 +20,12 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:pathData="M20,9L12,3L4,9V21H6V10L12,5.5L18,10V21H20V9Z"
+ android:pathData="M12,3L4,9V21H7V11H17V21H20V9L12,3Z"
android:fillColor="#FF000000" />
<path
- android:pathData="M7,11V21H17V11H7ZM15,13V15H9V13H15ZM9,19V17H15V19H9Z"
+ android:pathData="M15,13H9V15H15V13Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M15,17H9V19H15V17Z"
android:fillColor="#FF000000" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_outdoor_garden_off.xml b/packages/SystemUI/res/drawable/ic_device_outdoor_garden_off.xml
new file mode 100644
index 000000000000..0d98f9e2a6f3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_outdoor_garden_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M18,3L15,5.25L12,3L9,5.25L6,3L2,6V21H22V6L18,3ZM8,19H4V7L6,5.5L8,7V19ZM14,19H10V7L12,5.5L14,7V19ZM20,19H16V7L18,5.5L20,7V19Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_outdoor_garden_on.xml b/packages/SystemUI/res/drawable/ic_device_outdoor_garden_on.xml
new file mode 100644
index 000000000000..00b6af68272a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_outdoor_garden_on.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M2,6V21H8V6L5,3L2,6Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M9,6V21H15V6L12,3L9,6Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M19,3L16,6V21H22V6L19,3Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_pergola_on.xml b/packages/SystemUI/res/drawable/ic_device_pergola_on.xml
index b7113dcd042f..cbbee8c05e1b 100644
--- a/packages/SystemUI/res/drawable/ic_device_pergola_on.xml
+++ b/packages/SystemUI/res/drawable/ic_device_pergola_on.xml
@@ -20,7 +20,7 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:pathData="M20,2C19.7348,2 19.4804,2.1054 19.2929,2.293C19.1054,2.4805 19,2.7348 19,3V4H5V3C5,2.7348 4.8946,2.4805 4.7071,2.293C4.5196,2.1054 4.2652,2 4,2C3.7348,2 3.4804,2.1054 3.2929,2.293C3.1054,2.4805 3,2.7348 3,3V21H5V10H19V21H21V3C21,2.7348 20.8946,2.4805 20.7071,2.293C20.5196,2.1054 20.2652,2 20,2ZM5,8V6H19V8H5Z"
+ android:pathData="M20,2C19.7348,2 19.4804,2.1054 19.2929,2.293C19.1054,2.4805 19,2.7348 19,3V4H5V3C5,2.7348 4.8946,2.4805 4.7071,2.293C4.5196,2.1054 4.2652,2 4,2C3.7348,2 3.4804,2.1054 3.2929,2.293C3.1054,2.4805 3,2.7348 3,3V21H5V10H19V21H21V3C21,2.7348 20.8946,2.4805 20.7071,2.293C20.5196,2.1054 20.2652,2 20,2Z"
android:fillColor="#FF000000" />
<path
android:pathData="M8,18H11V21H13V18H16V16H8V18Z"
diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_off.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_off.xml
index 1ba8741e4ae2..27da211b315d 100644
--- a/packages/SystemUI/res/drawable/ic_device_thermostat_off.xml
+++ b/packages/SystemUI/res/drawable/ic_device_thermostat_off.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:pathData="M16,18.97C16.6469,18.1148 16.9979,17.0723 17,16C16.9993,15.2239 16.8183,14.4586 16.4712,13.7644C16.1241,13.0702 15.6205,12.4662 15,12V6C15,5.2043 14.6839,4.4413 14.1213,3.8787C13.5587,3.3161 12.7956,3 12,3C11.2044,3 10.4413,3.3161 9.8787,3.8787C9.3161,4.4413 9,5.2043 9,6V12C8.3795,12.4662 7.8759,13.0702 7.5288,13.7644C7.1817,14.4586 7.0007,15.2239 7,16C7.0021,17.0723 7.3531,18.1148 8,18.97V19H8.02C8.4815,19.6206 9.0818,20.1246 9.7729,20.4719C10.4639,20.8192 11.2266,21.0001 12,21.0001C12.7734,21.0001 13.5361,20.8192 14.2271,20.4719C14.9182,20.1246 15.5185,19.6206 15.98,19H16V18.97ZM10.2,13.6L11,13V6C11,5.7348 11.1054,5.4804 11.2929,5.2929C11.4804,5.1054 11.7348,5 12,5C12.2652,5 12.5196,5.1054 12.7071,5.2929C12.8946,5.4804 13,5.7348 13,6V13L13.8,13.6C14.1711,13.8809 14.4723,14.2435 14.6805,14.6598C14.8886,15.076 14.9979,15.5346 15,16H9C9.0009,15.5344 9.1098,15.0754 9.318,14.659C9.5262,14.2426 9.8281,13.8801 10.2,13.6Z"
+ android:pathData="M15,12V6C15,5.2043 14.6839,4.4413 14.1213,3.8787C13.5587,3.3161 12.7956,3 12,3C11.2044,3 10.4413,3.3161 9.8787,3.8787C9.3161,4.4413 9,5.2043 9,6V12C8.3795,12.4662 7.8759,13.0702 7.5288,13.7644C7.1817,14.4586 7.0007,15.2239 7,16C7.0021,17.0723 7.3531,18.1148 8,18.97V19H8.02C8.4815,19.6206 9.0818,20.1246 9.7729,20.4719C10.4639,20.8192 11.2266,21.0001 12,21.0001C12.7734,21.0001 13.5361,20.8192 14.2271,20.4719C14.9182,20.1246 15.5185,19.6206 15.98,19H16V18.97C16.6469,18.1148 16.9979,17.0723 17,16C16.9993,15.2239 16.8183,14.4586 16.4712,13.7644C16.1241,13.0702 15.6205,12.4662 15,12ZM14.4,17.773L14.367,17.819C14.0952,18.1807 13.744,18.4752 13.3405,18.6799C12.937,18.8846 12.4919,18.9941 12.0395,18.9998C11.5871,19.0055 11.1394,18.9073 10.7309,18.7128C10.3223,18.5184 9.9638,18.2327 9.683,17.878L9.604,17.778C9.2154,17.2664 9.0034,16.6425 9,16C9.0016,15.5346 9.1108,15.0758 9.3189,14.6595C9.5271,14.2432 9.8286,13.8805 10.2,13.6L11,13V6C11,5.7348 11.1054,5.4804 11.2929,5.2929C11.4804,5.1054 11.7348,5 12,5C12.2652,5 12.5196,5.1054 12.7071,5.2929C12.8946,5.4804 13,5.7348 13,6V13L13.8,13.6C14.1714,13.8805 14.4729,14.2432 14.6811,14.6595C14.8892,15.0758 14.9984,15.5346 15,16C14.9967,16.6403 14.7862,17.2623 14.4,17.773V17.773Z"
android:fillColor="#FF000000" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_on.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_on.xml
index 1ba8741e4ae2..deabb0e45b3d 100644
--- a/packages/SystemUI/res/drawable/ic_device_thermostat_on.xml
+++ b/packages/SystemUI/res/drawable/ic_device_thermostat_on.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:pathData="M16,18.97C16.6469,18.1148 16.9979,17.0723 17,16C16.9993,15.2239 16.8183,14.4586 16.4712,13.7644C16.1241,13.0702 15.6205,12.4662 15,12V6C15,5.2043 14.6839,4.4413 14.1213,3.8787C13.5587,3.3161 12.7956,3 12,3C11.2044,3 10.4413,3.3161 9.8787,3.8787C9.3161,4.4413 9,5.2043 9,6V12C8.3795,12.4662 7.8759,13.0702 7.5288,13.7644C7.1817,14.4586 7.0007,15.2239 7,16C7.0021,17.0723 7.3531,18.1148 8,18.97V19H8.02C8.4815,19.6206 9.0818,20.1246 9.7729,20.4719C10.4639,20.8192 11.2266,21.0001 12,21.0001C12.7734,21.0001 13.5361,20.8192 14.2271,20.4719C14.9182,20.1246 15.5185,19.6206 15.98,19H16V18.97ZM10.2,13.6L11,13V6C11,5.7348 11.1054,5.4804 11.2929,5.2929C11.4804,5.1054 11.7348,5 12,5C12.2652,5 12.5196,5.1054 12.7071,5.2929C12.8946,5.4804 13,5.7348 13,6V13L13.8,13.6C14.1711,13.8809 14.4723,14.2435 14.6805,14.6598C14.8886,15.076 14.9979,15.5346 15,16H9C9.0009,15.5344 9.1098,15.0754 9.318,14.659C9.5262,14.2426 9.8281,13.8801 10.2,13.6Z"
+ android:pathData="M15,12V6C15,5.2043 14.6839,4.4413 14.1213,3.8787C13.5587,3.3161 12.7956,3 12,3C11.2044,3 10.4413,3.3161 9.8787,3.8787C9.3161,4.4413 9,5.2043 9,6V12C8.3795,12.4662 7.8759,13.0702 7.5288,13.7644C7.1817,14.4586 7.0007,15.2239 7,16C7.0021,17.0723 7.3531,18.1148 8,18.97V19H8.02C8.4815,19.6206 9.0818,20.1246 9.7729,20.4719C10.4639,20.8192 11.2266,21.0001 12,21.0001C12.7734,21.0001 13.5361,20.8192 14.2271,20.4719C14.9182,20.1246 15.5185,19.6206 15.98,19H16V18.97C16.6469,18.1148 16.9979,17.0723 17,16C16.9993,15.2239 16.8183,14.4586 16.4712,13.7644C16.1241,13.0702 15.6205,12.4662 15,12V12ZM11,6C11,5.7348 11.1054,5.4804 11.2929,5.2929C11.4804,5.1054 11.7348,5 12,5C12.2652,5 12.5196,5.1054 12.7071,5.2929C12.8946,5.4804 13,5.7348 13,6V10H11V6Z"
android:fillColor="#FF000000" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_tv_off.xml b/packages/SystemUI/res/drawable/ic_device_tv_off.xml
index dd91ed831bb8..f0c9b565acd6 100644
--- a/packages/SystemUI/res/drawable/ic_device_tv_off.xml
+++ b/packages/SystemUI/res/drawable/ic_device_tv_off.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:pathData="M20,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19V21H5L5.667,19H18.333L19,21H20V19C20.5299,18.9984 21.0377,18.7871 21.4125,18.4124C21.7872,18.0376 21.9984,17.5299 22,17V6C21.9984,5.47 21.7872,4.9624 21.4125,4.5876C21.0377,4.2129 20.5299,4.0016 20,4ZM20,17H4V6H20V17Z"
+ android:pathData="M20,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19V21H5L5.667,19H18.333L19,21H20V19C20.5299,18.9984 21.0377,18.7871 21.4125,18.4124C21.7872,18.0376 21.9984,17.5299 22,17V6C21.9984,5.47 21.7872,4.9624 21.4125,4.5876C21.0377,4.2129 20.5299,4.0016 20,4V4ZM20,17H4V6H20V17Z"
android:fillColor="#FF000000" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_tv_on.xml b/packages/SystemUI/res/drawable/ic_device_tv_on.xml
index dd91ed831bb8..ed625e9420de 100644
--- a/packages/SystemUI/res/drawable/ic_device_tv_on.xml
+++ b/packages/SystemUI/res/drawable/ic_device_tv_on.xml
@@ -20,6 +20,9 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:pathData="M20,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19V21H5L5.667,19H18.333L19,21H20V19C20.5299,18.9984 21.0377,18.7871 21.4125,18.4124C21.7872,18.0376 21.9984,17.5299 22,17V6C21.9984,5.47 21.7872,4.9624 21.4125,4.5876C21.0377,4.2129 20.5299,4.0016 20,4ZM20,17H4V6H20V17Z"
+ android:pathData="M20,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19V21H5L5.667,19H18.333L19,21H20V19C20.5299,18.9984 21.0377,18.7871 21.4125,18.4124C21.7872,18.0376 21.9984,17.5299 22,17V6C21.9984,5.47 21.7872,4.9624 21.4125,4.5876C21.0377,4.2129 20.5299,4.0016 20,4V4ZM20,17H4V6H20V17Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M19,7H5V16H19V7Z"
android:fillColor="#FF000000" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml
deleted file mode 100644
index 24e063506250..000000000000
--- a/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="#FF000000"
- android:pathData="M12,2l-5.5,9h11L12,2zM12,5.84L13.93,9h-3.87L12,5.84zM17.5,13c-2.49,0 -4.5,2.01 -4.5,4.5s2.01,4.5 4.5,4.5 4.5,-2.01 4.5,-4.5 -2.01,-4.5 -4.5,-4.5zM17.5,20c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5zM3,21.5h8v-8L3,13.5v8zM5,15.5h4v4L5,19.5v-4z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_unknown_off.xml b/packages/SystemUI/res/drawable/ic_device_unknown_off.xml
new file mode 100644
index 000000000000..55820d0b5f63
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_unknown_off.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M8,6.5V11.76C8.8027,12.2963 9.4117,13.0766 9.7369,13.9856C10.0622,14.8946 10.0865,15.8841 9.8062,16.8079C9.526,17.7317 8.9561,18.541 8.1807,19.1161C7.4052,19.6912 6.4654,20.0017 5.5,20.0017C4.5346,20.0017 3.5948,19.6912 2.8193,19.1161C2.0439,18.541 1.474,17.7317 1.1938,16.8079C0.9135,15.8841 0.9378,14.8946 1.2631,13.9856C1.5883,13.0766 2.1973,12.2963 3,11.76V6.5C3,5.837 3.2634,5.2011 3.7322,4.7322C4.2011,4.2634 4.837,4 5.5,4C6.163,4 6.7989,4.2634 7.2678,4.7322C7.7366,5.2011 8,5.837 8,6.5ZM3.049,16H7.949C8.0467,15.5132 7.9978,15.0084 7.8084,14.5495C7.619,14.0905 7.2976,13.6981 6.885,13.422L5.998,12.828V6.5C5.998,6.3674 5.9453,6.2402 5.8516,6.1465C5.7578,6.0527 5.6306,6 5.498,6C5.3654,6 5.2382,6.0527 5.1445,6.1465C5.0507,6.2402 4.998,6.3674 4.998,6.5V12.828L4.111,13.422C3.7061,13.6922 3.3887,14.0746 3.1976,14.5223C3.0065,14.97 2.95,15.4637 3.035,15.943C3.0363,15.951 3.0389,15.959 3.0416,15.967C3.0453,15.978 3.049,15.989 3.049,16Z"
+ android:fillColor="#FF000000"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M14,16H20V18H14V16Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M17,4C15.7113,4.0006 14.457,4.416 13.4228,5.1849C12.3886,5.9538 11.6294,7.0353 11.2577,8.2692C10.8859,9.5031 10.9214,10.824 11.3587,12.0362C11.7961,13.2484 12.6121,14.2876 13.686,15H20.314C21.3879,14.2876 22.204,13.2484 22.6413,12.0362C23.0786,10.824 23.1141,9.5031 22.7423,8.2692C22.3706,7.0353 21.6114,5.9538 20.5772,5.1849C19.543,4.416 18.2887,4.0006 17,4ZM19.643,13H14.357C13.7469,12.4629 13.3149,11.7528 13.1185,10.9641C12.9221,10.1753 12.9707,9.3455 13.2577,8.5851C13.5447,7.8246 14.0566,7.1697 14.7251,6.7074C15.3937,6.2452 16.1872,5.9976 17,5.9976C17.8128,5.9976 18.6063,6.2452 19.2749,6.7074C19.9434,7.1697 20.4553,7.8246 20.7423,8.5851C21.0293,9.3455 21.0779,10.1753 20.8815,10.9641C20.6851,11.7528 20.2531,12.4629 19.643,13Z"
+ android:fillColor="#FF000000"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M18.0607,19.5607C17.7793,19.842 17.3978,20 17,20C16.6022,20 16.2207,19.842 15.9393,19.5607C15.658,19.2794 15.5,18.8978 15.5,18.5H18.5C18.5,18.8978 18.342,19.2794 18.0607,19.5607Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_unknown_on.xml b/packages/SystemUI/res/drawable/ic_device_unknown_on.xml
new file mode 100644
index 000000000000..08d9817389d2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_unknown_on.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M8,6.5V11.76C8.8027,12.2963 9.4117,13.0766 9.7369,13.9856C10.0622,14.8946 10.0865,15.8841 9.8062,16.8079C9.526,17.7317 8.9561,18.541 8.1807,19.1161C7.4052,19.6912 6.4654,20.0017 5.5,20.0017C4.5346,20.0017 3.5948,19.6912 2.8193,19.1161C2.0439,18.541 1.474,17.7317 1.1938,16.8079C0.9135,15.8841 0.9378,14.8946 1.2631,13.9856C1.5883,13.0766 2.1973,12.2963 3,11.76V6.5C3,5.837 3.2634,5.2011 3.7322,4.7322C4.2011,4.2634 4.837,4 5.5,4C6.163,4 6.7989,4.2634 7.2678,4.7322C7.7366,5.2011 8,5.837 8,6.5ZM3.049,16H7.949C8.0469,15.5134 7.9983,15.0087 7.8093,14.5498C7.6202,14.0909 7.2992,13.6984 6.887,13.422L6,12.828V6.5C6,6.3674 5.9473,6.2402 5.8535,6.1465C5.7598,6.0527 5.6326,6 5.5,6C5.3674,6 5.2402,6.0527 5.1465,6.1465C5.0527,6.2402 5,6.3674 5,6.5V12.828L4.111,13.422C3.7061,13.6922 3.3887,14.0746 3.1976,14.5223C3.0065,14.97 2.95,15.4637 3.035,15.943C3.0363,15.951 3.0389,15.959 3.0416,15.967C3.0453,15.978 3.049,15.989 3.049,16Z"
+ android:fillColor="#FF000000"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M20,16V18H14V16H20Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M17,4C15.7113,4.0006 14.457,4.416 13.4228,5.1849C12.3886,5.9538 11.6294,7.0353 11.2577,8.2692C10.8859,9.5031 10.9214,10.824 11.3587,12.0362C11.796,13.2484 12.6121,14.2876 13.686,15H20.314C21.3879,14.2876 22.204,13.2484 22.6413,12.0362C23.0786,10.824 23.1141,9.5031 22.7423,8.2692C22.3706,7.0353 21.6114,5.9538 20.5772,5.1849C19.543,4.416 18.2887,4.0006 17,4Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M18.0607,19.5607C17.7794,19.842 17.3978,20 17,20C16.6022,20 16.2206,19.842 15.9393,19.5607C15.658,19.2794 15.5,18.8978 15.5,18.5H18.5C18.5,18.8978 18.342,19.2794 18.0607,19.5607Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_water_off.xml b/packages/SystemUI/res/drawable/ic_device_water_off.xml
new file mode 100644
index 000000000000..e1a7846af49a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_water_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M17.654,7.563L12,2L6.346,7.563C5.6036,8.2877 5.0136,9.1533 4.6108,10.1094C4.2079,11.0654 4.0002,12.0924 4,13.1299C4,15.2516 4.8429,17.2863 6.3432,18.7866C7.8434,20.2869 9.8783,21.1299 12,21.1299C14.1217,21.1299 16.1566,20.2869 17.6569,18.7866C19.1572,17.2863 20,15.2516 20,13.1299C20,12.0924 19.7925,11.0654 19.3896,10.1094C18.9867,9.1533 18.3966,8.2875 17.654,7.563ZM12,19C10.4265,19.0152 8.9113,18.4056 7.7865,17.3052C6.6617,16.2048 6.0192,14.7033 6,13.1299C5.9996,12.3577 6.1541,11.5933 6.4543,10.8818C6.7546,10.1704 7.1945,9.5262 7.748,8.9878L12,4.8061L16.252,8.9888C16.8056,9.5269 17.2456,10.171 17.5458,10.8823C17.8461,11.5936 18.0005,12.3578 18,13.1299C17.9807,14.7033 17.3383,16.2048 16.2135,17.3052C15.0887,18.4056 13.5735,19.0152 12,19Z"
+ android:fillColor="#FF000000" />
+ <path
+ android:pathData="M16,12C15.7348,12 15.4804,12.1054 15.2929,12.293C15.1054,12.4805 15,12.7348 15,13C15,13.7956 14.6839,14.5585 14.1213,15.1211C13.5587,15.6837 12.7956,16 12,16C11.7348,16 11.4804,16.1054 11.2929,16.293C11.1054,16.4805 11,16.7348 11,17C11,17.2652 11.1054,17.5195 11.2929,17.707C11.4804,17.8946 11.7348,18 12,18C13.3256,17.9984 14.5964,17.471 15.5338,16.5337C16.4711,15.5964 16.9984,14.3256 17,13C17,12.7348 16.8946,12.4805 16.7071,12.293C16.5196,12.1054 16.2652,12 16,12Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_water_on.xml b/packages/SystemUI/res/drawable/ic_device_water_on.xml
new file mode 100644
index 000000000000..e57e053b8d4d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_water_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M17.654,7.563L12,2L6.346,7.563C5.6036,8.2877 5.0136,9.1533 4.6108,10.1094C4.2079,11.0654 4.0002,12.0924 4,13.1299C4.0174,15.2343 4.87,17.2458 6.3703,18.7217C7.8705,20.1975 9.8956,21.017 12,21C14.1044,21.017 16.1295,20.1975 17.6297,18.7217C19.13,17.2458 19.9826,15.2343 20,13.1299C20,12.0924 19.7925,11.0654 19.3896,10.1094C18.9867,9.1533 18.3966,8.2875 17.654,7.563ZM12,18C11.7348,18 11.4804,17.8946 11.2929,17.707C11.1054,17.5195 11,17.2652 11,17C11,16.7348 11.1054,16.4805 11.2929,16.293C11.4804,16.1054 11.7348,16 12,16C12.7956,16 13.5587,15.6837 14.1213,15.1211C14.6839,14.5585 15,13.7956 15,13C15,12.7348 15.1054,12.4805 15.2929,12.293C15.4804,12.1054 15.7348,12 16,12C16.2652,12 16.5196,12.1054 16.7071,12.293C16.8946,12.4805 17,12.7348 17,13C16.9984,14.3256 16.4711,15.5964 15.5338,16.5337C14.5964,17.471 13.3256,17.9984 12,18Z"
+ android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
index 91beeb88d87f..623f2a0d3423 100644
--- a/packages/SystemUI/res/layout/controls_with_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -72,6 +72,6 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="30dp"
- android:layout_marginLeft="@dimen/controls_list_side_margin"
- android:layout_marginRight="@dimen/controls_list_side_margin" />
+ android:layout_marginLeft="@dimen/global_actions_side_margin"
+ android:layout_marginRight="@dimen/global_actions_side_margin" />
</merge>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml
index cb53fe619b24..72cc2ddba282 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml
@@ -18,15 +18,16 @@
work around this for now with LinearLayouts. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="@dimen/global_actions_grid_item_vertical_margin"
android:paddingBottom="@dimen/global_actions_grid_item_vertical_margin"
android:paddingLeft="@dimen/global_actions_grid_item_side_margin"
android:paddingRight="@dimen/global_actions_grid_item_side_margin"
- android:layout_marginRight="3dp"
- android:layout_marginLeft="3dp"
+ android:layout_marginRight="@dimen/control_base_item_margin"
+ android:layout_marginLeft="@dimen/control_base_item_margin"
android:background="@drawable/control_background">
<LinearLayout
android:layout_width="@dimen/global_actions_grid_item_width"
diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
index 620e2e6b509c..59c4d011166a 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
@@ -12,17 +12,17 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:theme="@style/qs_theme"
- android:gravity="top | center_horizontal"
+ android:gravity="top"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_marginTop="@dimen/global_actions_top_margin"
>
<LinearLayout
android:id="@android:id/list"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
- android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+ android:layout_marginLeft="@dimen/global_actions_side_margin"
+ android:layout_marginRight="@dimen/global_actions_side_margin"
android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
android:paddingTop="@dimen/global_actions_grid_vertical_padding"
diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml
index fc3bf941b27a..e5ac5f89cd25 100644
--- a/packages/SystemUI/res/layout/qs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qs_media_panel.xml
@@ -23,7 +23,8 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal|fill_vertical"
- android:padding="16dp"
+ android:paddingTop="@dimen/qs_media_panel_outer_padding"
+ android:paddingBottom="@dimen/qs_media_panel_outer_padding"
android:background="@drawable/qs_media_background"
>
@@ -42,7 +43,9 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="16dp"
+ android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+ android:paddingStart="@dimen/qs_media_panel_outer_padding"
+ android:paddingEnd="16dp"
>
<ImageView
@@ -139,6 +142,7 @@
<!-- Seek Bar -->
<SeekBar
android:id="@+id/media_progress_bar"
+ style="@android:style/Widget.ProgressBar.Horizontal"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -154,6 +158,9 @@
android:id="@+id/notification_media_progress_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:paddingStart="@dimen/qs_media_panel_outer_padding"
+ android:paddingEnd="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginBottom="10dp"
android:layout_gravity="center"
>
<!-- width is set to "match_parent" to avoid extra layout calls -->
@@ -184,6 +191,8 @@
android:layoutDirection="ltr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:paddingStart="@dimen/qs_media_panel_outer_padding"
+ android:paddingEnd="@dimen/qs_media_panel_outer_padding"
android:gravity="center"
>
<ImageButton
diff --git a/packages/SystemUI/res/values-sw320dp/dimens.xml b/packages/SystemUI/res/values-sw320dp/dimens.xml
index 47a2a093302f..c110113e91f4 100644
--- a/packages/SystemUI/res/values-sw320dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw320dp/dimens.xml
@@ -31,6 +31,6 @@
<dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen>
<!-- Home Controls -->
- <dimen name="controls_list_side_margin">10dp</dimen>
+ <dimen name="global_actions_side_margin">10dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw360dp/dimens.xml b/packages/SystemUI/res/values-sw360dp/dimens.xml
index 35a653608a90..fc510bf03efd 100644
--- a/packages/SystemUI/res/values-sw360dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw360dp/dimens.xml
@@ -27,6 +27,6 @@
<dimen name="navigation_side_padding">40dip</dimen>
<!-- Home Controls -->
- <dimen name="controls_list_side_margin">12dp</dimen>
+ <dimen name="global_actions_side_margin">12dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw392dp/dimens.xml b/packages/SystemUI/res/values-sw392dp/dimens.xml
index 308bc69656da..4c9d02e30bfa 100644
--- a/packages/SystemUI/res/values-sw392dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw392dp/dimens.xml
@@ -31,6 +31,6 @@
<dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen>
<!-- Home Controls -->
- <dimen name="controls_list_side_margin">16dp</dimen>
+ <dimen name="global_actions_side_margin">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 288487acec79..4482cdac3327 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -50,6 +50,10 @@
<!-- The color of the text in the Global Actions menu -->
<color name="global_actions_alert_text">@color/GM2_red_700</color>
+ <!-- The color of the background of the emergency button when home controls are visible -->
+ <color name="global_actions_emergency_background">@color/GM2_red_400</color>
+ <color name="global_actions_emergency_text">@color/GM2_grey_100</color>
+
<!-- Tint color for the content on the notification overflow card. -->
<color name="keyguard_overflow_content_color">#ff686868</color>
@@ -211,6 +215,7 @@
<color name="GM2_red_50">#FCE8E6</color>
<color name="GM2_red_200">#F6AEA9</color>
<color name="GM2_red_300">#F28B82</color>
+ <color name="GM2_red_400">#EE675C</color>
<color name="GM2_red_500">#B71C1C</color>
<color name="GM2_red_700">#C5221F</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bce5fac76cfc..6a8a4b9ac2fd 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1008,6 +1008,9 @@
<dimen name="global_actions_grid_container_shadow_offset">20dp</dimen>
<dimen name="global_actions_grid_container_negative_shadow_offset">-20dp</dimen>
+ <!-- Margins at the left and right of the power menu and home controls widgets. -->
+ <dimen name="global_actions_side_margin">16dp</dimen>
+
<!-- The maximum offset in either direction that elements are moved horizontally to prevent
burn-in on AOD. -->
<dimen name="burn_in_prevention_offset_x">8dp</dimen>
@@ -1211,6 +1214,7 @@
<!-- Size of media cards in the QSPanel carousel -->
<dimen name="qs_media_width">350dp</dimen>
<dimen name="qs_media_padding">8dp</dimen>
+ <dimen name="qs_media_panel_outer_padding">16dp</dimen>
<dimen name="qs_media_corner_radius">10dp</dimen>
<dimen name="qs_media_album_size">72dp</dimen>
<dimen name="qs_seamless_icon_size">20dp</dimen>
@@ -1231,7 +1235,6 @@
<dimen name="controls_header_side_margin">4dp</dimen>
<dimen name="controls_header_menu_size">48dp</dimen>
<dimen name="controls_header_app_icon_size">40dp</dimen>
- <dimen name="controls_list_side_margin">16dp</dimen>
<dimen name="controls_top_margin">44dp</dimen>
<dimen name="control_header_text_size">22sp</dimen>
<dimen name="control_text_size">14sp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index aabee1c952e8..3e02b30c376b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -511,7 +511,7 @@
</style>
<style name="TextAppearance.NotificationInfo">
- <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textColor">@color/notification_primary_text_color</item>
</style>
@@ -521,7 +521,6 @@
</style>
<style name="TextAppearance.NotificationInfo.Title">
- <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:textColor">@color/notification_primary_text_color</item>
<item name="android:textStyle">bold</item>
</style>
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 7eb5a8f2b06c..e99245fa438f 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -34,6 +34,8 @@ import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.NoSuchElementException;
+
/**
* Encapsulates all logic for secondary lockscreen state management.
*/
@@ -79,7 +81,9 @@ public class AdminSecondaryLockScreenController {
private final IKeyguardCallback mCallback = new IKeyguardCallback.Stub() {
@Override
public void onDismiss() {
- dismiss(UserHandle.getCallingUserId());
+ mHandler.post(() -> {
+ dismiss(UserHandle.getCallingUserId());
+ });
}
@Override
@@ -91,7 +95,9 @@ public class AdminSecondaryLockScreenController {
if (surfacePackage != null) {
mView.setChildSurfacePackage(surfacePackage);
} else {
- dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ mHandler.post(() -> {
+ dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ });
}
}
};
@@ -122,6 +128,7 @@ public class AdminSecondaryLockScreenController {
// If the remote content is not readied within the timeout period,
// move on without the secondary lockscreen.
dismiss(userId);
+ Log.w(TAG, "Timed out waiting for secondary lockscreen content.");
},
REMOTE_CONTENT_READY_TIMEOUT_MILLIS);
}
@@ -150,8 +157,12 @@ public class AdminSecondaryLockScreenController {
* Displays the Admin security Surface view.
*/
public void show(Intent serviceIntent) {
- mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
- mParent.addView(mView);
+ if (mClient == null) {
+ mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
+ }
+ if (!mView.isAttachedToWindow()) {
+ mParent.addView(mView);
+ }
}
/**
@@ -162,7 +173,11 @@ public class AdminSecondaryLockScreenController {
mParent.removeView(mView);
}
if (mClient != null) {
- mClient.asBinder().unlinkToDeath(mKeyguardClientDeathRecipient, 0);
+ try {
+ mClient.asBinder().unlinkToDeath(mKeyguardClientDeathRecipient, 0);
+ } catch (NoSuchElementException e) {
+ Log.w(TAG, "IKeyguardClient death recipient already released");
+ }
mContext.unbindService(mConnection);
mClient = null;
}
@@ -185,10 +200,12 @@ public class AdminSecondaryLockScreenController {
private void dismiss(int userId) {
mHandler.removeCallbacksAndMessages(null);
- if (mView != null && mView.isAttachedToWindow()
- && userId == KeyguardUpdateMonitor.getCurrentUser()) {
+ if (mView.isAttachedToWindow() && userId == KeyguardUpdateMonitor.getCurrentUser()) {
hide();
- mKeyguardCallback.dismiss(true, userId);
+ if (mKeyguardCallback != null) {
+ mKeyguardCallback.dismiss(/* securityVerified= */ true, userId,
+ /* bypassSecondaryLockScreen= */true);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index d5a08dda9853..aa2fe3c7f8fc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -85,7 +85,8 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback {
// the user proved presence via some other way to the trust agent.
Log.i(TAG, "TrustAgent dismissed Keyguard.");
}
- dismiss(false /* authenticated */, userId);
+ dismiss(false /* authenticated */, userId,
+ /* bypassSecondaryLockScreen */ false);
} else {
mViewMediatorCallback.playTrustedSound();
}
@@ -190,7 +191,7 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback {
* @return True if the keyguard is done.
*/
public boolean dismiss(int targetUserId) {
- return dismiss(false, targetUserId);
+ return dismiss(false, targetUserId, false);
}
public boolean handleBackKey() {
@@ -206,8 +207,10 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback {
}
@Override
- public boolean dismiss(boolean authenticated, int targetUserId) {
- return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId);
+ public boolean dismiss(boolean authenticated, int targetUserId,
+ boolean bypassSecondaryLockScreen) {
+ return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId,
+ bypassSecondaryLockScreen);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
index 49dcfffb0d72..e38472745234 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -25,6 +25,15 @@ public interface KeyguardSecurityCallback {
void dismiss(boolean securityVerified, int targetUserId);
/**
+ * Dismiss the given security screen.
+ * @param securityVerified true if the user correctly entered credentials for the given screen.
+ * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
+ * @param bypassSecondaryLockScreen true if the user can bypass the secondary lock screen,
+ * if any, during this dismissal.
+ */
+ void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen);
+
+ /**
* Manually report user activity to keep the device awake.
*/
void userActivity();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ba8a1a945a77..1e1ce4e6d159 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -115,7 +115,8 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
- public boolean dismiss(boolean authenticated, int targetUserId);
+ public boolean dismiss(boolean authenticated, int targetUserId,
+ boolean bypassSecondaryLockScreen);
public void userActivity();
public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
@@ -504,9 +505,12 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
* @param authenticated true if the user entered the correct authentication
* @param targetUserId a user that needs to be the foreground user at the finish (if called)
* completion.
+ * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
+ * secondary lock screen requirement, if any.
* @return true if keyguard is done
*/
- boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId) {
+ boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
+ boolean bypassSecondaryLockScreen) {
if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
boolean finish = false;
boolean strongAuth = false;
@@ -555,7 +559,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
}
}
// Check for device admin specified additional security measures.
- if (finish) {
+ if (finish && !bypassSecondaryLockScreen) {
Intent secondaryLockscreenIntent =
mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
if (secondaryLockscreenIntent != null) {
@@ -636,8 +640,15 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
mUpdateMonitor.cancelFaceAuth();
}
+ @Override
public void dismiss(boolean authenticated, int targetId) {
- mSecurityCallback.dismiss(authenticated, targetId);
+ dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
+ }
+
+ @Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) {
+ mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
}
public boolean isVerifyUnlockOnly() {
@@ -689,6 +700,9 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
@Override
public void dismiss(boolean securityVerified, int targetUserId) { }
@Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) { }
+ @Override
public void onUserInput() { }
@Override
public void reset() {}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 88d6943b4071..367058fa58dd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -57,7 +57,6 @@ import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
-import android.media.AudioManager;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IRemoteCallback;
@@ -82,6 +81,8 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import androidx.lifecycle.Observer;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
@@ -97,6 +98,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.RingerModeTracker;
import com.google.android.collect.Lists;
@@ -258,6 +260,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private TrustManager mTrustManager;
private UserManager mUserManager;
private KeyguardBypassController mKeyguardBypassController;
+ private RingerModeTracker mRingerModeTracker;
private int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
private LockPatternUtils mLockPatternUtils;
@@ -295,6 +298,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final Handler mHandler;
+ private final Observer<Integer> mRingerModeObserver = new Observer<Integer>() {
+ @Override
+ public void onChanged(Integer ringer) {
+ mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, ringer, 0).sendToTarget();
+ }
+ };
+
private SparseBooleanArray mFaceSettingEnabledForUser = new SparseBooleanArray();
private BiometricManager mBiometricManager;
private IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback =
@@ -1145,9 +1155,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
mHandler.obtainMessage(MSG_SIM_STATE_CHANGE, args.subId, args.slotId, args.simState)
.sendToTarget();
- } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED,
- intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0));
} else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
@@ -1501,6 +1508,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mUserTrustIsUsuallyManaged.delete(userId);
}
+ private void registerRingerTracker() {
+ mRingerModeTracker.getRingerMode().observeForever(mRingerModeObserver);
+ }
+
@VisibleForTesting
@Inject
protected KeyguardUpdateMonitor(
@@ -1508,6 +1519,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Main Looper mainLooper,
BroadcastDispatcher broadcastDispatcher,
DumpManager dumpManager,
+ RingerModeTracker ringerModeTracker,
@Background Executor backgroundExecutor) {
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
@@ -1515,6 +1527,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged);
mBackgroundExecutor = backgroundExecutor;
mBroadcastDispatcher = broadcastDispatcher;
+ mRingerModeTracker = ringerModeTracker;
dumpManager.registerDumpable(getClass().getName(), this);
mHandler = new Handler(mainLooper) {
@@ -1648,10 +1661,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
filter.addAction(Intent.ACTION_SERVICE_STATE);
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
- filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, mHandler);
+ mHandler.post(this::registerRingerTracker);
+
final IntentFilter allUserFilter = new IntentFilter();
allUserFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
allUserFilter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
@@ -2802,6 +2816,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
mBroadcastDispatcher.unregisterReceiver(mBroadcastAllReceiver);
+ mRingerModeTracker.getRingerMode().removeObserver(mRingerModeObserver);
mHandler.removeCallbacksAndMessages(null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
index 362014f51e36..e17d4e69093a 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
@@ -25,6 +25,7 @@ import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.util.time.SystemClock;
/**
* Extends the lifetime of foreground notification services such that they show for at least
@@ -39,8 +40,10 @@ public class ForegroundServiceLifetimeExtender implements NotificationLifetimeEx
private NotificationSafeToRemoveCallback mNotificationSafeToRemoveCallback;
private ArraySet<NotificationEntry> mManagedEntries = new ArraySet<>();
private Handler mHandler = new Handler(Looper.getMainLooper());
+ private final SystemClock mSystemClock;
- public ForegroundServiceLifetimeExtender() {
+ public ForegroundServiceLifetimeExtender(SystemClock systemClock) {
+ mSystemClock = systemClock;
}
@Override
@@ -55,8 +58,8 @@ public class ForegroundServiceLifetimeExtender implements NotificationLifetimeEx
return false;
}
- long currentTime = System.currentTimeMillis();
- return currentTime - entry.getSbn().getPostTime() < MIN_FGS_TIME_MS;
+ long currentTime = mSystemClock.uptimeMillis();
+ return currentTime - entry.getCreationTime() < MIN_FGS_TIME_MS;
}
@Override
@@ -84,7 +87,7 @@ public class ForegroundServiceLifetimeExtender implements NotificationLifetimeEx
}
};
long delayAmt = MIN_FGS_TIME_MS
- - (System.currentTimeMillis() - entry.getSbn().getPostTime());
+ - (mSystemClock.uptimeMillis() - entry.getCreationTime());
mHandler.postDelayed(r, delayAmt);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index f01fa811c529..ef1f4e0afede 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.util.time.SystemClock;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -49,7 +50,8 @@ public class ForegroundServiceNotificationListener {
public ForegroundServiceNotificationListener(Context context,
ForegroundServiceController foregroundServiceController,
NotificationEntryManager notificationEntryManager,
- NotifPipeline notifPipeline) {
+ NotifPipeline notifPipeline,
+ SystemClock systemClock) {
mContext = context;
mForegroundServiceController = foregroundServiceController;
@@ -76,7 +78,8 @@ public class ForegroundServiceNotificationListener {
removeNotification(entry.getSbn());
}
});
- mEntryManager.addNotificationLifetimeExtender(new ForegroundServiceLifetimeExtender());
+ mEntryManager.addNotificationLifetimeExtender(
+ new ForegroundServiceLifetimeExtender(systemClock));
notifPipeline.addCollectionListener(new NotifCollectionListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 9d885fd3c207..669a86b8a742 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -599,7 +599,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (mStackView == null) {
mStackView = new BubbleStackView(
mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
- mSysUiState);
+ mSysUiState, mNotificationShadeWindowController);
ViewGroup nsv = mNotificationShadeWindowController.getNotificationShadeView();
int bubbleScrimIndex = nsv.indexOfChild(nsv.findViewById(R.id.scrim_for_bubble));
int stackIndex = bubbleScrimIndex + 1; // Show stack above bubble scrim.
@@ -719,13 +719,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/**
- * Tell the stack of bubbles to expand.
- */
- public void expandStack() {
- mBubbleData.setExpanded(true);
- }
-
- /**
* Tell the stack of bubbles to collapse.
*/
public void collapseStack() {
@@ -753,12 +746,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed;
}
- @VisibleForTesting
- void selectBubble(String key) {
- Bubble bubble = mBubbleData.getBubbleWithKey(key);
- mBubbleData.setSelectedBubble(bubble);
- }
-
void promoteBubbleFromOverflow(Bubble bubble) {
bubble.setInflateSynchronously(mInflateSynchronously);
mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
@@ -778,13 +765,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/**
- * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
- */
- void dismissStack(@DismissReason int reason) {
- mBubbleData.dismissAll(reason);
- }
-
- /**
* Directs a back gesture at the bubble stack. When opened, the current expanded bubble
* is forwarded a back key down/up pair.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index be9cd5f01c86..4c149ddd3939 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.service.notification.NotificationListenerService;
import android.util.Log;
import android.util.Pair;
+import android.view.View;
import androidx.annotation.Nullable;
@@ -751,6 +752,7 @@ public class BubbleData {
}
@VisibleForTesting(visibility = PRIVATE)
+ @Nullable
Bubble getBubbleWithKey(String key) {
for (int i = 0; i < mBubbles.size(); i++) {
Bubble bubble = mBubbles.get(i);
@@ -761,6 +763,17 @@ public class BubbleData {
return null;
}
+ @Nullable
+ Bubble getBubbleWithView(View view) {
+ for (int i = 0; i < mBubbles.size(); i++) {
+ Bubble bubble = mBubbles.get(i);
+ if (bubble.getIconView() != null && bubble.getIconView().equals(view)) {
+ return bubble;
+ }
+ }
+ return null;
+ }
+
@VisibleForTesting(visibility = PRIVATE)
Bubble getOverflowBubbleWithKey(String key) {
for (int i = 0; i < mOverflowBubbles.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 496456deccee..93fb6972fad5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,6 +16,7 @@
package com.android.systemui.bubbles;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.view.Display.INVALID_DISPLAY;
@@ -126,6 +127,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
0 /* enterResId */, 0 /* exitResId */);
options.setTaskAlwaysOnTop(true);
+ options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
// Post to keep the lifecycle normal
post(() -> {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 044feaa117c8..61e6f39c054f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -30,6 +30,7 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
@@ -43,6 +44,7 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.Region;
import android.os.Bundle;
import android.os.Vibrator;
import android.util.Log;
@@ -81,8 +83,10 @@ import com.android.systemui.bubbles.animation.StackAnimationController;
import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.util.RelativeTouchListener;
import com.android.systemui.util.animation.PhysicsAnimator;
import com.android.systemui.util.magnetictarget.MagnetizedObject;
@@ -239,7 +243,6 @@ public class BubbleStackView extends FrameLayout {
mExpandedAnimationController.dump(fd, pw, args);
}
- private BubbleTouchHandler mTouchHandler;
private BubbleController.BubbleExpandListener mExpandListener;
private SysUiState mSysUiState;
@@ -296,7 +299,7 @@ public class BubbleStackView extends FrameLayout {
@Override
public void setValue(Object o, float v) {
- onFlyoutDragged(v);
+ setFlyoutStateForDragLength(v);
}
};
@@ -328,6 +331,8 @@ public class BubbleStackView extends FrameLayout {
@NonNull
private final SurfaceSynchronizer mSurfaceSynchronizer;
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
+
/**
* The currently magnetized object, which is being dragged and will be attracted to the magnetic
* dismiss target.
@@ -337,13 +342,6 @@ public class BubbleStackView extends FrameLayout {
private MagnetizedObject<?> mMagnetizedObject;
/**
- * The action to run when the magnetized object is released in the dismiss target.
- *
- * This will actually perform the dismissal of either the stack or an individual bubble.
- */
- private Runnable mReleasedInDismissTargetAction;
-
- /**
* The MagneticTarget instance for our circular dismiss view. This is added to the
* MagnetizedObject instances for the stack and any dragged-out bubbles.
*/
@@ -377,7 +375,7 @@ public class BubbleStackView extends FrameLayout {
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
mExpandedAnimationController.dismissDraggedOutBubble(
mExpandedAnimationController.getDraggedOutBubble(),
- mReleasedInDismissTargetAction);
+ BubbleStackView.this::dismissMagnetizedObject);
hideDismissTarget();
}
};
@@ -410,7 +408,7 @@ public class BubbleStackView extends FrameLayout {
mStackAnimationController.implodeStack(
() -> {
resetDesaturationAndDarken();
- mReleasedInDismissTargetAction.run();
+ dismissMagnetizedObject();
}
);
@@ -418,6 +416,197 @@ public class BubbleStackView extends FrameLayout {
}
};
+ /**
+ * Click listener set on each bubble view. When collapsed, clicking a bubble expands the stack.
+ * When expanded, clicking a bubble either expands that bubble, or collapses the stack.
+ */
+ private OnClickListener mBubbleClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ final Bubble clickedBubble = mBubbleData.getBubbleWithView(view);
+
+ // If the bubble has since left us, ignore the click.
+ if (clickedBubble == null) {
+ return;
+ }
+
+ final boolean clickedBubbleIsCurrentlyExpandedBubble =
+ clickedBubble.getKey().equals(mExpandedBubble.getKey());
+
+ if (isExpanded() && !clickedBubbleIsCurrentlyExpandedBubble) {
+ if (clickedBubble != mBubbleData.getSelectedBubble()) {
+ // Select the clicked bubble.
+ mBubbleData.setSelectedBubble(clickedBubble);
+ } else {
+ // If the clicked bubble is the selected bubble (but not the expanded bubble),
+ // that means overflow was previously expanded. Set the selected bubble
+ // internally without going through BubbleData (which would ignore it since it's
+ // already selected).
+ setSelectedBubble(clickedBubble);
+
+ }
+ } else {
+ // Otherwise, we either tapped the stack (which means we're collapsed
+ // and should expand) or the currently selected bubble (we're expanded
+ // and should collapse).
+ if (!maybeShowStackUserEducation()) {
+ mBubbleData.setExpanded(!mBubbleData.isExpanded());
+ }
+ }
+ }
+ };
+
+ /**
+ * Touch listener set on each bubble view. This enables dragging and dismissing the stack (when
+ * collapsed), or individual bubbles (when expanded).
+ */
+ private RelativeTouchListener mBubbleTouchListener = new RelativeTouchListener() {
+
+ @Override
+ public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) {
+ // If we're expanding or collapsing, consume but ignore all touch events.
+ if (mIsExpansionAnimating) {
+ return true;
+ }
+
+ if (mBubbleData.isExpanded()) {
+ maybeShowManageEducation(false /* show */);
+
+ // If we're expanded, tell the animation controller to prepare to drag this bubble,
+ // dispatching to the individual bubble magnet listener.
+ mExpandedAnimationController.prepareForBubbleDrag(
+ v /* bubble */,
+ mMagneticTarget,
+ mIndividualBubbleMagnetListener);
+
+ // Save the magnetized individual bubble so we can dispatch touch events to it.
+ mMagnetizedObject = mExpandedAnimationController.getMagnetizedBubbleDraggingOut();
+ } else {
+ // If we're collapsed, prepare to drag the stack. Cancel active animations, set the
+ // animation controller, and hide the flyout.
+ mStackAnimationController.cancelStackPositionAnimations();
+ mBubbleContainer.setActiveController(mStackAnimationController);
+ hideFlyoutImmediate();
+
+ // Also, save the magnetized stack so we can dispatch touch events to it.
+ mMagnetizedObject = mStackAnimationController.getMagnetizedStack(mMagneticTarget);
+ mMagnetizedObject.setMagnetListener(mStackMagnetListener);
+ }
+
+ passEventToMagnetizedObject(ev);
+
+ // Bubbles are always interested in all touch events!
+ return true;
+ }
+
+ @Override
+ public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy) {
+ // If we're expanding or collapsing, ignore all touch events.
+ if (mIsExpansionAnimating) {
+ return;
+ }
+
+ // Show the dismiss target, if we haven't already.
+ springInDismissTargetMaybe();
+
+ // First, see if the magnetized object consumes the event - if so, we shouldn't move the
+ // bubble since it's stuck to the target.
+ if (!passEventToMagnetizedObject(ev)) {
+ if (mBubbleData.isExpanded()) {
+ mExpandedAnimationController.dragBubbleOut(
+ v, viewInitialX + dx, viewInitialY + dy);
+ } else {
+ hideStackUserEducation(false /* fromExpansion */);
+ mStackAnimationController.moveStackFromTouch(
+ viewInitialX + dx, viewInitialY + dy);
+ }
+ }
+ }
+
+ @Override
+ public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy, float velX, float velY) {
+ // If we're expanding or collapsing, ignore all touch events.
+ if (mIsExpansionAnimating) {
+ return;
+ }
+
+ // First, see if the magnetized object consumes the event - if so, the bubble was
+ // released in the target or flung out of it, and we should ignore the event.
+ if (!passEventToMagnetizedObject(ev)) {
+ if (mBubbleData.isExpanded()) {
+ mExpandedAnimationController.snapBubbleBack(v, velX, velY);
+ } else {
+ // Fling the stack to the edge, and save whether or not it's going to end up on
+ // the left side of the screen.
+ mStackOnLeftOrWillBe =
+ mStackAnimationController.flingStackThenSpringToEdge(
+ viewInitialX + dx, velX, velY) <= 0;
+
+ updateBubbleZOrdersAndDotPosition(true /* animate */);
+
+ logBubbleEvent(null /* no bubble associated with bubble stack move */,
+ SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
+ }
+
+ hideDismissTarget();
+ }
+ }
+ };
+
+ /** Click listener set on the flyout, which expands the stack when the flyout is tapped. */
+ private OnClickListener mFlyoutClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (maybeShowStackUserEducation()) {
+ // If we're showing user education, don't open the bubble show the education first
+ mBubbleToExpandAfterFlyoutCollapse = null;
+ } else {
+ mBubbleToExpandAfterFlyoutCollapse = mBubbleData.getSelectedBubble();
+ }
+
+ mFlyout.removeCallbacks(mHideFlyout);
+ mHideFlyout.run();
+ }
+ };
+
+ /** Touch listener for the flyout. This enables the drag-to-dismiss gesture on the flyout. */
+ private RelativeTouchListener mFlyoutTouchListener = new RelativeTouchListener() {
+
+ @Override
+ public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) {
+ mFlyout.removeCallbacks(mHideFlyout);
+ return true;
+ }
+
+ @Override
+ public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy) {
+ setFlyoutStateForDragLength(dx);
+ }
+
+ @Override
+ public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy, float velX, float velY) {
+ final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
+ final boolean metRequiredVelocity =
+ onLeft ? velX < -FLYOUT_DISMISS_VELOCITY : velX > FLYOUT_DISMISS_VELOCITY;
+ final boolean metRequiredDeltaX =
+ onLeft
+ ? dx < -mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS
+ : dx > mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS;
+ final boolean isCancelFling = onLeft ? velX > 0 : velX < 0;
+ final boolean shouldDismiss = metRequiredVelocity
+ || (metRequiredDeltaX && !isCancelFling);
+
+ mFlyout.removeCallbacks(mHideFlyout);
+ animateFlyoutCollapsed(shouldDismiss, velX);
+
+ maybeShowStackUserEducation();
+ }
+ };
+
private ViewGroup mDismissTargetContainer;
private PhysicsAnimator<View> mDismissTargetAnimator;
private PhysicsAnimator.SpringConfig mDismissTargetSpring = new PhysicsAnimator.SpringConfig(
@@ -436,18 +625,19 @@ public class BubbleStackView extends FrameLayout {
private BubbleManageEducationView mManageEducationView;
private boolean mAnimatingManageEducationAway;
+ @SuppressLint("ClickableViewAccessibility")
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator,
- SysUiState sysUiState) {
+ SysUiState sysUiState,
+ NotificationShadeWindowController notificationShadeWindowController) {
super(context);
mBubbleData = data;
mInflater = LayoutInflater.from(context);
- mTouchHandler = new BubbleTouchHandler(this, data, context);
- setOnTouchListener(mTouchHandler);
mSysUiState = sysUiState;
+ mNotificationShadeWindowController = notificationShadeWindowController;
Resources res = getResources();
mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
@@ -641,6 +831,18 @@ public class BubbleStackView extends FrameLayout {
mDesaturateAndDarkenPaint.setColorFilter(new ColorMatrixColorFilter(animatedMatrix));
mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint);
});
+
+ // If the stack itself is touched, it means none of its touchable views (bubbles, flyouts,
+ // ActivityViews, etc.) were touched. Collapse the stack if it's expanded.
+ setOnTouchListener((view, ev) -> {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ if (mBubbleData.isExpanded()) {
+ mBubbleData.setExpanded(false);
+ }
+ }
+
+ return false;
+ });
}
private void setUpUserEducation() {
@@ -690,6 +892,7 @@ public class BubbleStackView extends FrameLayout {
}
}
+ @SuppressLint("ClickableViewAccessibility")
private void setUpFlyout() {
if (mFlyout != null) {
removeView(mFlyout);
@@ -699,6 +902,8 @@ public class BubbleStackView extends FrameLayout {
mFlyout.animate()
.setDuration(FLYOUT_ALPHA_ANIMATION_DURATION)
.setInterpolator(new AccelerateDecelerateInterpolator());
+ mFlyout.setOnClickListener(mFlyoutClickListener);
+ mFlyout.setOnTouchListener(mFlyoutTouchListener);
addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
}
@@ -718,6 +923,7 @@ public class BubbleStackView extends FrameLayout {
mBubbleContainer.addView(mBubbleOverflow.getBtn(), overflowBtnIndex,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ mBubbleOverflow.getBtn().setOnClickListener(v -> setSelectedBubble(mBubbleOverflow));
}
/**
* Handle theme changes.
@@ -920,6 +1126,7 @@ public class BubbleStackView extends FrameLayout {
}
// via BubbleData.Listener
+ @SuppressLint("ClickableViewAccessibility")
void addBubble(Bubble bubble) {
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "addBubble: " + bubble);
@@ -944,6 +1151,9 @@ public class BubbleStackView extends FrameLayout {
bubble.getIconView().setDotPositionOnLeft(
!mStackOnLeftOrWillBe /* onLeft */, false /* animate */);
+ bubble.getIconView().setOnClickListener(mBubbleClickListener);
+ bubble.getIconView().setOnTouchListener(mBubbleTouchListener);
+
mBubbleContainer.addView(bubble.getIconView(), 0,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
ViewClippingUtil.setClippingDeactivated(bubble.getIconView(), true, mClippingParameters);
@@ -1009,10 +1219,6 @@ public class BubbleStackView extends FrameLayout {
updatePointerPosition();
}
- void showOverflow() {
- setSelectedBubble(mBubbleOverflow);
- }
-
/**
* Changes the currently selected bubble. If the stack is already expanded, the newly selected
* bubble will be shown immediately. This does not change the expanded state or change the
@@ -1177,14 +1383,6 @@ public class BubbleStackView extends FrameLayout {
}
}
- /*
- * Sets the action to run to dismiss the currently dragging object (either the stack or an
- * individual bubble).
- */
- public void setReleasedInDismissTargetAction(Runnable action) {
- mReleasedInDismissTargetAction = action;
- }
-
/**
* Dismiss the stack of bubbles.
*
@@ -1201,54 +1399,6 @@ public class BubbleStackView extends FrameLayout {
}
/**
- * @return the view the touch event is on
- */
- @Nullable
- public View getTargetView(MotionEvent event) {
- float x = event.getRawX();
- float y = event.getRawY();
- if (mIsExpanded) {
- if (isIntersecting(mBubbleContainer, x, y)) {
- if (BubbleExperimentConfig.allowBubbleOverflow(mContext)
- && isIntersecting(mBubbleOverflow.getBtn(), x, y)) {
- return mBubbleOverflow.getBtn();
- }
- // Could be tapping or dragging a bubble while expanded
- for (int i = 0; i < getBubbleCount(); i++) {
- BadgedImageView view = (BadgedImageView) mBubbleContainer.getChildAt(i);
- if (isIntersecting(view, x, y)) {
- return view;
- }
- }
- }
- BubbleExpandedView bev = (BubbleExpandedView) mExpandedViewContainer.getChildAt(0);
- if (bev.intersectingTouchableContent((int) x, (int) y)) {
- return bev;
- }
- // Outside of the parts we care about.
- return null;
- } else if (mFlyout.getVisibility() == VISIBLE && isIntersecting(mFlyout, x, y)) {
- return mFlyout;
- } else if (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) {
- View bubbleChild = mBubbleContainer.getChildAt(0);
- if (isIntersecting(bubbleChild, x, y)) {
- return this;
- } else if (isIntersecting(mUserEducationView, x, y)) {
- return mUserEducationView;
- } else {
- return null;
- }
- }
-
- // If it wasn't an individual bubble in the expanded state, or the flyout, it's the stack.
- return this;
- }
-
- View getFlyoutView() {
- return mFlyout;
- }
-
- /**
* @deprecated use {@link #setExpanded(boolean)} and
* {@link BubbleData#setSelectedBubble(Bubble)}
*/
@@ -1385,124 +1535,74 @@ public class BubbleStackView extends FrameLayout {
}
}
- /** Called when the collapsed stack is tapped on. */
- void onStackTapped() {
- if (!maybeShowStackUserEducation()) {
- mBubbleData.setExpanded(true);
- }
- }
-
- /** Called when a drag operation on an individual bubble has started. */
- public void onBubbleDragStart(View bubble) {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "onBubbleDragStart: bubble=" + ((BadgedImageView) bubble).getKey());
- }
-
- if (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView())) {
- return;
- }
-
- mExpandedAnimationController.prepareForBubbleDrag(bubble, mMagneticTarget);
-
- // We're dragging an individual bubble, so set the magnetized object to the magnetized
- // bubble.
- mMagnetizedObject = mExpandedAnimationController.getMagnetizedBubbleDraggingOut();
- mMagnetizedObject.setMagnetListener(mIndividualBubbleMagnetListener);
-
- maybeShowManageEducation(false);
- }
-
- /** Called with the coordinates to which an individual bubble has been dragged. */
- public void onBubbleDragged(View bubble, float x, float y) {
- if (!mIsExpanded || mIsExpansionAnimating
- || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) {
- return;
- }
-
- mExpandedAnimationController.dragBubbleOut(bubble, x, y);
- springInDismissTarget();
- }
-
- /** Called when a drag operation on an individual bubble has finished. */
- public void onBubbleDragFinish(
- View bubble, float x, float y, float velX, float velY) {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "onBubbleDragFinish: bubble=" + bubble);
- }
-
- if (!mIsExpanded || mIsExpansionAnimating
- || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) {
- return;
- }
-
- mExpandedAnimationController.snapBubbleBack(bubble, velX, velY);
- hideDismissTarget();
- }
-
- /** Expands the clicked bubble. */
- public void expandBubble(Bubble bubble) {
- if (bubble != null && bubble.equals(mBubbleData.getSelectedBubble())) {
- // If the bubble we're supposed to expand is the selected bubble, that means the
- // overflow bubble is currently expanded. Don't tell BubbleData to set this bubble as
- // selected, since it already is. Just call the stack's setSelectedBubble to expand it.
- setSelectedBubble(bubble);
- } else {
- mBubbleData.setSelectedBubble(bubble);
- }
- }
-
- void onDragStart() {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "onDragStart()");
- }
- if (mIsExpanded || mIsExpansionAnimating) {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "mIsExpanded or mIsExpansionAnimating");
- }
- return;
+ /**
+ * This method is called by {@link android.app.ActivityView} because the BubbleStackView has a
+ * higher Z-index than the ActivityView (so that dragged-out bubbles are visible over the AV).
+ * ActivityView is asking BubbleStackView to subtract the stack's bounds from the provided
+ * touchable region, so that the ActivityView doesn't consume events meant for the stack. Due to
+ * the special nature of ActivityView, it does not respect the standard
+ * {@link #dispatchTouchEvent} and {@link #onInterceptTouchEvent} methods typically used for
+ * this purpose.
+ *
+ * BubbleStackView is MATCH_PARENT, so that bubbles can be positioned via their translation
+ * properties for performance reasons. This means that the default implementation of this method
+ * subtracts the entirety of the screen from the ActivityView's touchable region, resulting in
+ * it not receiving any touch events. This was previously addressed by returning false in the
+ * stack's {@link View#canReceivePointerEvents()} method, but this precluded the use of any
+ * touch handlers in the stack or its child views.
+ *
+ * To support touch handlers, we're overriding this method to leave the ActivityView's touchable
+ * region alone. The only touchable part of the stack that can ever overlap the AV is a
+ * dragged-out bubble that is animating back into the row of bubbles. It's not worth continually
+ * updating the touchable region to allow users to grab a bubble while it completes its ~50ms
+ * animation back to the bubble row.
+ *
+ * NOTE: Any future additions to the stack that obscure the ActivityView region will need their
+ * bounds subtracted here in order to receive touch events.
+ */
+ @Override
+ public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
+ // If the notification shade is expanded, we shouldn't let the ActivityView steal any touch
+ // events from any location.
+ if (mNotificationShadeWindowController.getPanelExpanded()) {
+ touchableRegion.setEmpty();
}
- mStackAnimationController.cancelStackPositionAnimations();
- mBubbleContainer.setActiveController(mStackAnimationController);
- hideFlyoutImmediate();
-
- // Since we're dragging the stack, set the magnetized object to the magnetized stack.
- mMagnetizedObject = mStackAnimationController.getMagnetizedStack(mMagneticTarget);
- mMagnetizedObject.setMagnetListener(mStackMagnetListener);
}
- void onDragged(float x, float y) {
- if (mIsExpanded || mIsExpansionAnimating) {
- return;
- }
-
- hideStackUserEducation(false /* fromExpansion */);
- springInDismissTarget();
- mStackAnimationController.moveStackFromTouch(x, y);
+ /**
+ * If you're here because you're not receiving touch events on a view that is a descendant of
+ * BubbleStackView, and you think BSV is intercepting them - it's not! You need to subtract the
+ * bounds of the view in question in {@link #subtractObscuredTouchableRegion}. The ActivityView
+ * consumes all touch events within its bounds, even for views like the BubbleStackView that are
+ * above it. It ignores typical view touch handling methods like this one and
+ * dispatchTouchEvent.
+ */
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return super.onInterceptTouchEvent(ev);
}
- void onDragFinish(float x, float y, float velX, float velY) {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "onDragFinish");
- }
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ boolean dispatched = super.dispatchTouchEvent(ev);
- if (mIsExpanded || mIsExpansionAnimating) {
- return;
+ // If a new bubble arrives while the collapsed stack is being dragged, it will be positioned
+ // at the front of the stack (under the touch position). Subsequent ACTION_MOVE events will
+ // then be passed to the new bubble, which will not consume them since it hasn't received an
+ // ACTION_DOWN yet. Work around this by passing MotionEvents directly to the touch handler
+ // until the current gesture ends with an ACTION_UP event.
+ if (!dispatched && !mIsExpanded && mIsGestureInProgress) {
+ dispatched = mBubbleTouchListener.onTouch(this /* view */, ev);
}
- final float newStackX = mStackAnimationController.flingStackThenSpringToEdge(x, velX, velY);
- logBubbleEvent(null /* no bubble associated with bubble stack move */,
- SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
-
- mStackOnLeftOrWillBe = newStackX <= 0;
- updateBubbleZOrdersAndDotPosition(true /* animate */);
- hideDismissTarget();
- }
+ mIsGestureInProgress =
+ ev.getAction() != MotionEvent.ACTION_UP
+ && ev.getAction() != MotionEvent.ACTION_CANCEL;
- void onFlyoutDragStart() {
- mFlyout.removeCallbacks(mHideFlyout);
+ return dispatched;
}
- void onFlyoutDragged(float deltaX) {
+ void setFlyoutStateForDragLength(float deltaX) {
// This shouldn't happen, but if it does, just wait until the flyout lays out. This method
// is continually called.
if (mFlyout.getWidth() <= 0) {
@@ -1538,59 +1638,27 @@ public class BubbleStackView extends FrameLayout {
mFlyout.setTranslationX(mFlyout.getRestingTranslationX() + overscrollTranslation);
}
- void onFlyoutTapped() {
- if (maybeShowStackUserEducation()) {
- // If we're showing user education, don't open the bubble show the education first
- mBubbleToExpandAfterFlyoutCollapse = null;
- } else {
- mBubbleToExpandAfterFlyoutCollapse = mBubbleData.getSelectedBubble();
- }
-
- mFlyout.removeCallbacks(mHideFlyout);
- mHideFlyout.run();
- }
-
- /**
- * Called when the flyout drag has finished, and returns true if the gesture successfully
- * dismissed the flyout.
- */
- void onFlyoutDragFinished(float deltaX, float velX) {
- final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
- final boolean metRequiredVelocity =
- onLeft ? velX < -FLYOUT_DISMISS_VELOCITY : velX > FLYOUT_DISMISS_VELOCITY;
- final boolean metRequiredDeltaX =
- onLeft
- ? deltaX < -mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS
- : deltaX > mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS;
- final boolean isCancelFling = onLeft ? velX > 0 : velX < 0;
- final boolean shouldDismiss = metRequiredVelocity || (metRequiredDeltaX && !isCancelFling);
-
- mFlyout.removeCallbacks(mHideFlyout);
- animateFlyoutCollapsed(shouldDismiss, velX);
-
- maybeShowStackUserEducation();
+ /** Passes the MotionEvent to the magnetized object and returns true if it was consumed. */
+ private boolean passEventToMagnetizedObject(MotionEvent event) {
+ return mMagnetizedObject != null && mMagnetizedObject.maybeConsumeMotionEvent(event);
}
/**
- * Called when the first touch event of a gesture (stack drag, bubble drag, flyout drag, etc.)
- * is received.
+ * Dismisses the magnetized object - either an individual bubble, if we're expanded, or the
+ * stack, if we're collapsed.
*/
- void onGestureStart() {
- mIsGestureInProgress = true;
- }
-
- /** Called when a gesture is completed or cancelled. */
- void onGestureFinished() {
- mIsGestureInProgress = false;
-
+ private void dismissMagnetizedObject() {
if (mIsExpanded) {
- mExpandedAnimationController.onGestureFinished();
- }
- }
+ final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject();
+ final Bubble draggedOutBubble = mBubbleData.getBubbleWithView(draggedOutBubbleView);
- /** Passes the MotionEvent to the magnetized object and returns true if it was consumed. */
- boolean passEventToMagnetizedObject(MotionEvent event) {
- return mMagnetizedObject != null && mMagnetizedObject.maybeConsumeMotionEvent(event);
+ if (mBubbleData.hasBubbleWithKey(draggedOutBubble.getKey())) {
+ mBubbleData.notificationEntryRemoved(
+ draggedOutBubble.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ }
+ } else {
+ mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
+ }
}
/** Prepares and starts the desaturate/darken animation on the bubble stack. */
@@ -1624,7 +1692,7 @@ public class BubbleStackView extends FrameLayout {
}
/** Animates in the dismiss target. */
- private void springInDismissTarget() {
+ private void springInDismissTargetMaybe() {
if (mShowingDismiss) {
return;
}
@@ -1827,13 +1895,6 @@ public class BubbleStackView extends FrameLayout {
return 0;
}
- private boolean isIntersecting(View view, float x, float y) {
- mTempLoc = view.getLocationOnScreen();
- mTempRect.set(mTempLoc[0], mTempLoc[1], mTempLoc[0] + view.getWidth(),
- mTempLoc[1] + view.getHeight());
- return mTempRect.contains(x, y);
- }
-
private void requestUpdate() {
if (mViewUpdatedRequested || mIsExpansionAnimating) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
deleted file mode 100644
index 132c45fab3d2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import com.android.systemui.Dependency;
-
-/**
- * Handles interpreting touches on a {@link BubbleStackView}. This includes expanding, collapsing,
- * dismissing, and flings.
- */
-class BubbleTouchHandler implements View.OnTouchListener {
-
- private final PointF mTouchDown = new PointF();
- private final PointF mViewPositionOnTouchDown = new PointF();
- private final BubbleStackView mStack;
- private final BubbleData mBubbleData;
-
- private BubbleController mController = Dependency.get(BubbleController.class);
-
- private boolean mMovedEnough;
- private int mTouchSlopSquared;
- private VelocityTracker mVelocityTracker;
-
- /** View that was initially touched, when we received the first ACTION_DOWN event. */
- private View mTouchedView;
-
- BubbleTouchHandler(BubbleStackView stackView,
- BubbleData bubbleData, Context context) {
- final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mTouchSlopSquared = touchSlop * touchSlop;
- mBubbleData = bubbleData;
- mStack = stackView;
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- final int action = event.getActionMasked();
-
- // If we aren't currently in the process of touching a view, figure out what we're touching.
- // It'll be the stack, an individual bubble, or nothing.
- if (mTouchedView == null) {
- mTouchedView = mStack.getTargetView(event);
- }
-
- // If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching
- // anything, collapse the stack.
- if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
- mBubbleData.setExpanded(false);
- mStack.hideStackUserEducation(false /* fromExpansion */);
- resetForNextGesture();
- return false;
- }
-
- if (!(mTouchedView instanceof BadgedImageView)
- && !(mTouchedView instanceof BubbleStackView)
- && !(mTouchedView instanceof BubbleFlyoutView)) {
-
- // Not touching anything touchable, but we shouldn't collapse (e.g. touching edge
- // of expanded view).
- mStack.maybeShowManageEducation(false);
- resetForNextGesture();
- return false;
- }
-
- final boolean isStack = mStack.equals(mTouchedView);
- final boolean isFlyout = mStack.getFlyoutView().equals(mTouchedView);
- final float rawX = event.getRawX();
- final float rawY = event.getRawY();
-
- // The coordinates of the touch event, in terms of the touched view's position.
- final float viewX = mViewPositionOnTouchDown.x + rawX - mTouchDown.x;
- final float viewY = mViewPositionOnTouchDown.y + rawY - mTouchDown.y;
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- trackMovement(event);
-
- mTouchDown.set(rawX, rawY);
- mStack.onGestureStart();
-
- if (isStack) {
- mViewPositionOnTouchDown.set(mStack.getStackPosition());
-
- // Dismiss the entire stack if it's released in the dismiss target.
- mStack.setReleasedInDismissTargetAction(
- () -> mController.dismissStack(BubbleController.DISMISS_USER_GESTURE));
- mStack.onDragStart();
- mStack.passEventToMagnetizedObject(event);
- } else if (isFlyout) {
- mStack.onFlyoutDragStart();
- } else {
- mViewPositionOnTouchDown.set(
- mTouchedView.getTranslationX(), mTouchedView.getTranslationY());
-
- // Dismiss only the dragged-out bubble if it's released in the target.
- final String individualBubbleKey = ((BadgedImageView) mTouchedView).getKey();
- mStack.setReleasedInDismissTargetAction(() -> {
- final Bubble bubble =
- mBubbleData.getBubbleWithKey(individualBubbleKey);
- // bubble can be null if the user is in the middle of
- // dismissing the bubble, but the app also sent a cancel
- if (bubble != null) {
- mController.removeBubble(bubble.getEntry(),
- BubbleController.DISMISS_USER_GESTURE);
- }
- });
-
- mStack.onBubbleDragStart(mTouchedView);
- mStack.passEventToMagnetizedObject(event);
- }
-
- break;
- case MotionEvent.ACTION_MOVE:
- trackMovement(event);
- final float deltaX = rawX - mTouchDown.x;
- final float deltaY = rawY - mTouchDown.y;
-
- if ((deltaX * deltaX) + (deltaY * deltaY) > mTouchSlopSquared && !mMovedEnough) {
- mMovedEnough = true;
- }
-
- if (mMovedEnough) {
- if (isFlyout) {
- mStack.onFlyoutDragged(deltaX);
- } else if (!mStack.passEventToMagnetizedObject(event)) {
- // If the magnetic target doesn't consume the event, drag the stack or
- // bubble.
- if (isStack) {
- mStack.onDragged(viewX, viewY);
- } else {
- mStack.onBubbleDragged(mTouchedView, viewX, viewY);
- }
- }
- }
- break;
-
- case MotionEvent.ACTION_CANCEL:
- resetForNextGesture();
- break;
-
- case MotionEvent.ACTION_UP:
- trackMovement(event);
- mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000);
- final float velX = mVelocityTracker.getXVelocity();
- final float velY = mVelocityTracker.getYVelocity();
-
- if (isFlyout && mMovedEnough) {
- mStack.onFlyoutDragFinished(rawX - mTouchDown.x /* deltaX */, velX);
- } else if (isFlyout) {
- if (!mBubbleData.isExpanded() && !mMovedEnough) {
- mStack.onFlyoutTapped();
- }
- } else if (mMovedEnough) {
- if (!mStack.passEventToMagnetizedObject(event)) {
- // If the magnetic target didn't consume the event, tell the stack to finish
- // the drag.
- if (isStack) {
- mStack.onDragFinish(viewX, viewY, velX, velY);
- } else {
- mStack.onBubbleDragFinish(mTouchedView, viewX, viewY, velX, velY);
- }
- }
- } else if (mTouchedView == mStack.getExpandedBubbleView()) {
- mBubbleData.setExpanded(false);
- } else if (isStack) {
- mStack.onStackTapped();
- } else {
- final String key = ((BadgedImageView) mTouchedView).getKey();
- if (key == BubbleOverflow.KEY) {
- mStack.showOverflow();
- } else {
- mStack.expandBubble(mBubbleData.getBubbleWithKey(key));
- }
- }
- resetForNextGesture();
- break;
- }
-
- return true;
- }
-
- /** Clears all touch-related state. */
- private void resetForNextGesture() {
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- mTouchedView = null;
- mMovedEnough = false;
-
- mStack.onGestureFinished();
- }
-
- private void trackMovement(MotionEvent event) {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(event);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index a0b49384d49f..d974adc34ee0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -252,8 +252,14 @@ public class ExpandedAnimationController
mSpringToTouchOnNextMotionEvent = true;
}
- /** Prepares the given bubble to be dragged out. */
- public void prepareForBubbleDrag(View bubble, MagnetizedObject.MagneticTarget target) {
+ /**
+ * Prepares the given bubble view to be dragged out, using the provided magnetic target and
+ * listener.
+ */
+ public void prepareForBubbleDrag(
+ View bubble,
+ MagnetizedObject.MagneticTarget target,
+ MagnetizedObject.MagnetListener listener) {
mLayout.cancelAnimationsOnView(bubble);
bubble.setTranslationZ(Short.MAX_VALUE);
@@ -277,6 +283,7 @@ public class ExpandedAnimationController
}
};
mMagnetizedBubbleDraggingOut.addTarget(target);
+ mMagnetizedBubbleDraggingOut.setMagnetListener(listener);
mMagnetizedBubbleDraggingOut.setHapticsEnabled(true);
mMagnetizedBubbleDraggingOut.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
index c292769f1066..b1bbafc1ed8f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
@@ -1117,9 +1117,4 @@ public class PhysicsAnimationLayout extends FrameLayout {
mAssociatedController = controller;
}
}
-
- @Override
- protected boolean canReceivePointerEvents() {
- return false;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 5d03fc51004d..7e8fec716b1f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -45,7 +45,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
companion object {
private const val TAG = "ControlsBindingControllerImpl"
private const val MAX_CONTROLS_REQUEST = 100000L
- private const val SUGGESTED_CONTROLS_REQUEST = 4L
+ private const val SUGGESTED_CONTROLS_REQUEST = 6L
}
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
@@ -61,6 +61,11 @@ open class ControlsBindingControllerImpl @Inject constructor(
*/
private var statefulControlSubscriber: StatefulControlSubscriber? = null
+ /*
+ * Will track any active load subscriber. Only one can be active at any time.
+ */
+ private var loadSubscriber: LoadSubscriber? = null
+
private val actionCallbackService = object : IControlsActionCallback.Stub() {
override fun accept(
token: IBinder,
@@ -99,17 +104,24 @@ open class ControlsBindingControllerImpl @Inject constructor(
component: ComponentName,
callback: ControlsBindingController.LoadCallback
): Runnable {
- val subscriber = LoadSubscriber(callback, MAX_CONTROLS_REQUEST)
- retrieveLifecycleManager(component).maybeBindAndLoad(subscriber)
- return subscriber.loadCancel()
+ loadSubscriber?.loadCancel()
+
+ val ls = LoadSubscriber(callback, MAX_CONTROLS_REQUEST)
+ loadSubscriber = ls
+
+ retrieveLifecycleManager(component).maybeBindAndLoad(ls)
+ return ls.loadCancel()
}
override fun bindAndLoadSuggested(
component: ComponentName,
callback: ControlsBindingController.LoadCallback
) {
- val subscriber = LoadSubscriber(callback, SUGGESTED_CONTROLS_REQUEST)
- retrieveLifecycleManager(component).maybeBindAndLoadSuggested(subscriber)
+ loadSubscriber?.loadCancel()
+ val ls = LoadSubscriber(callback, SUGGESTED_CONTROLS_REQUEST)
+ loadSubscriber = ls
+
+ retrieveLifecycleManager(component).maybeBindAndLoadSuggested(ls)
}
override fun subscribe(structureInfo: StructureInfo) {
@@ -152,13 +164,16 @@ open class ControlsBindingControllerImpl @Inject constructor(
override fun changeUser(newUser: UserHandle) {
if (newUser == currentUser) return
- unsubscribe()
unbind()
- currentProvider = null
currentUser = newUser
}
private fun unbind() {
+ unsubscribe()
+
+ loadSubscriber?.loadCancel()
+ loadSubscriber = null
+
currentProvider?.unbindService()
currentProvider = null
}
@@ -210,6 +225,20 @@ open class ControlsBindingControllerImpl @Inject constructor(
val callback: ControlsBindingController.LoadCallback
) : CallbackRunnable(token) {
override fun doRun() {
+ Log.d(TAG, "LoadSubscription: Complete and loading controls")
+ callback.accept(list)
+ }
+ }
+
+ private inner class OnCancelAndLoadRunnable(
+ token: IBinder,
+ val list: List<Control>,
+ val subscription: IControlsSubscription,
+ val callback: ControlsBindingController.LoadCallback
+ ) : CallbackRunnable(token) {
+ override fun doRun() {
+ Log.d(TAG, "LoadSubscription: Canceling and loading controls")
+ provider?.cancelSubscription(subscription)
callback.accept(list)
}
}
@@ -220,6 +249,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
val requestLimit: Long
) : CallbackRunnable(token) {
override fun doRun() {
+ Log.d(TAG, "LoadSubscription: Starting subscription")
provider?.startSubscription(subscription, requestLimit)
}
}
@@ -254,34 +284,54 @@ open class ControlsBindingControllerImpl @Inject constructor(
val requestLimit: Long
) : IControlsSubscriber.Stub() {
val loadedControls = ArrayList<Control>()
- var hasError = false
+ private var isTerminated = false
private var _loadCancelInternal: (() -> Unit)? = null
+ private lateinit var subscription: IControlsSubscription
+
fun loadCancel() = Runnable {
- Log.d(TAG, "Cancel load requested")
- _loadCancelInternal?.invoke()
- }
+ Log.d(TAG, "Cancel load requested")
+ _loadCancelInternal?.invoke()
+ }
override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
- _loadCancelInternal = subs::cancel
+ subscription = subs
+ _loadCancelInternal = { currentProvider?.cancelSubscription(subscription) }
backgroundExecutor.execute(OnSubscribeRunnable(token, subs, requestLimit))
}
override fun onNext(token: IBinder, c: Control) {
- backgroundExecutor.execute { loadedControls.add(c) }
+ backgroundExecutor.execute {
+ if (isTerminated) return@execute
+
+ loadedControls.add(c)
+
+ // Once we have reached our requestLimit, send a request to cancel, and immediately
+ // load the results. Calls to onError() and onComplete() are not required after
+ // cancel.
+ if (loadedControls.size >= requestLimit) {
+ maybeTerminateAndRun(
+ OnCancelAndLoadRunnable(token, loadedControls, subscription, callback)
+ )
+ }
+ }
}
+
override fun onError(token: IBinder, s: String) {
- hasError = true
- _loadCancelInternal = {}
- currentProvider?.cancelLoadTimeout()
- backgroundExecutor.execute(OnLoadErrorRunnable(token, s, callback))
+ maybeTerminateAndRun(OnLoadErrorRunnable(token, s, callback))
}
override fun onComplete(token: IBinder) {
+ maybeTerminateAndRun(OnLoadRunnable(token, loadedControls, callback))
+ }
+
+ private fun maybeTerminateAndRun(postTerminateFn: Runnable) {
+ if (isTerminated) return
+
+ isTerminated = true
_loadCancelInternal = {}
- if (!hasError) {
- currentProvider?.cancelLoadTimeout()
- backgroundExecutor.execute(OnLoadRunnable(token, loadedControls, callback))
- }
+ currentProvider?.cancelLoadTimeout()
+
+ backgroundExecutor.execute(postTerminateFn)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index ae75dd4d94ab..568fb289027d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -180,6 +180,11 @@ interface ControlsController : UserAwareController {
fun countFavoritesForComponent(componentName: ComponentName): Int
/**
+ * TEMPORARY for testing
+ */
+ fun resetFavorites()
+
+ /**
* Interface for structure to pass data to [ControlsFavoritingActivity].
*/
interface LoadData {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 34833396acef..8805694616a4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -365,6 +365,8 @@ class ControlsControllerImpl @Inject constructor (
componentName: ComponentName,
callback: Consumer<Boolean>
) {
+ if (seedingInProgress) return
+
Log.i(TAG, "Beginning request to seed favorites for: $componentName")
if (!confirmAvailability()) {
if (userChanging) {
@@ -495,6 +497,13 @@ class ControlsControllerImpl @Inject constructor (
}
}
+ override fun resetFavorites() {
+ executor.execute {
+ Favorites.clear()
+ persistenceWrapper.storeFavorites(Favorites.getAllStructures())
+ }
+ }
+
override fun refreshStatus(componentName: ComponentName, control: Control) {
if (!confirmAvailability()) {
Log.d(TAG, "Controls not available")
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 895f1d218982..a6af6a11d8b7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -63,8 +63,6 @@ class ControlsProviderLifecycleManager(
) : IBinder.DeathRecipient {
val token: IBinder = Binder()
- @GuardedBy("subscriptions")
- private val subscriptions = mutableListOf<IControlsSubscription>()
private var requiresBound = false
@GuardedBy("queuedServiceMethods")
private val queuedServiceMethods: MutableSet<ServiceMethod> = ArraySet()
@@ -194,7 +192,7 @@ class ControlsProviderLifecycleManager(
* Request a call to [IControlsProvider.loadSuggested].
*
* If the service is not bound, the call will be queued and the service will be bound first.
- * The service will be unbound after the controls are returned or the call times out.
+ * The service will be unbound if the call times out.
*
* @param subscriber the subscriber that manages coordination for loading controls
*/
@@ -245,9 +243,7 @@ class ControlsProviderLifecycleManager(
if (DEBUG) {
Log.d(TAG, "startSubscription: $subscription")
}
- synchronized(subscriptions) {
- subscriptions.add(subscription)
- }
+
wrapper?.request(subscription, requestLimit)
}
@@ -261,9 +257,7 @@ class ControlsProviderLifecycleManager(
if (DEBUG) {
Log.d(TAG, "cancelSubscription: $subscription")
}
- synchronized(subscriptions) {
- subscriptions.remove(subscription)
- }
+
wrapper?.cancel(subscription)
}
@@ -281,17 +275,6 @@ class ControlsProviderLifecycleManager(
onLoadCanceller?.run()
onLoadCanceller = null
- // be sure to cancel all subscriptions
- val subs = synchronized(subscriptions) {
- ArrayList(subscriptions).also {
- subscriptions.clear()
- }
- }
-
- subs.forEach {
- wrapper?.cancel(it)
- }
-
bindService(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 208d9117e088..934a69508d68 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -16,18 +16,26 @@
package com.android.systemui.controls.ui
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ObjectAnimator
+import android.app.AlertDialog
import android.app.Dialog
import android.content.ComponentName
import android.content.Context
+import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Configuration
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
+import android.os.Process
import android.service.controls.Control
import android.service.controls.actions.ControlAction
import android.util.TypedValue
import android.util.Log
+import android.view.animation.AccelerateInterpolator
+import android.view.animation.DecelerateInterpolator
import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.view.View
@@ -77,6 +85,8 @@ class ControlsUiControllerImpl @Inject constructor (
private const val PREF_COMPONENT = "controls_component"
private const val PREF_STRUCTURE = "controls_structure"
+ private const val FADE_IN_MILLIS = 225L
+
private val EMPTY_COMPONENT = ComponentName("", "")
private val EMPTY_STRUCTURE = StructureInfo(
EMPTY_COMPONENT,
@@ -153,7 +163,20 @@ class ControlsUiControllerImpl @Inject constructor (
private fun reload(parent: ViewGroup) {
if (hidden) return
- show(parent)
+
+ val fadeAnim = ObjectAnimator.ofFloat(parent, "alpha", 1.0f, 0.0f)
+ fadeAnim.setInterpolator(AccelerateInterpolator(1.0f))
+ fadeAnim.setDuration(FADE_IN_MILLIS)
+ fadeAnim.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ show(parent)
+ val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
+ showAnim.setInterpolator(DecelerateInterpolator(1.0f))
+ showAnim.setDuration(FADE_IN_MILLIS)
+ showAnim.start()
+ }
+ })
+ fadeAnim.start()
}
private fun showSeedingView(items: List<SelectionItem>) {
@@ -229,7 +252,8 @@ class ControlsUiControllerImpl @Inject constructor (
private fun createMenu() {
val items = arrayOf(
- context.resources.getString(R.string.controls_menu_add)
+ context.resources.getString(R.string.controls_menu_add),
+ "Reset"
)
var adapter = ArrayAdapter<String>(context, R.layout.controls_more_item, items)
@@ -249,6 +273,8 @@ class ControlsUiControllerImpl @Inject constructor (
when (pos) {
// 0: Add Control
0 -> startFavoritingActivity(view.context, selectedStructure)
+ // 1: TEMPORARY for reset controls
+ 1 -> showResetConfirmation()
else -> Log.w(ControlsUiController.TAG,
"Unsupported index ($pos) on 'more' menu selection")
}
@@ -275,6 +301,39 @@ class ControlsUiControllerImpl @Inject constructor (
})
}
+ private fun showResetConfirmation() {
+ val builder = AlertDialog.Builder(
+ context,
+ android.R.style.Theme_DeviceDefault_Dialog_Alert
+ ).apply {
+ setMessage("For testing purposes: Would you like to " +
+ "reset your favorited device controls?")
+ setPositiveButton(
+ android.R.string.ok,
+ DialogInterface.OnClickListener { dialog, _ ->
+ val userHandle = Process.myUserHandle()
+ val userContext = context.createContextAsUser(userHandle, 0)
+ val prefs = userContext.getSharedPreferences(
+ "controls_prefs", Context.MODE_PRIVATE)
+ prefs.edit().putBoolean("ControlsSeedingCompleted", false).apply()
+ controlsController.get().resetFavorites()
+ dialog.dismiss()
+ context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
+ })
+ setNegativeButton(
+ android.R.string.cancel,
+ DialogInterface.OnClickListener {
+ dialog, _ -> dialog.cancel()
+ }
+ )
+ }
+ builder.create().apply {
+ getWindow().apply {
+ setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+ }
+ }.show()
+ }
+
private fun createDropDown(items: List<SelectionItem>) {
items.forEach {
RenderInfo.registerComponentIcon(it.componentName, it.icon)
@@ -370,7 +429,8 @@ class ControlsUiControllerImpl @Inject constructor (
}
// add spacers if necessary to keep control size consistent
- var spacersToAdd = selectedStructure.controls.size % maxColumns
+ val mod = selectedStructure.controls.size % maxColumns
+ var spacersToAdd = if (mod == 0) 0 else maxColumns - mod
while (spacersToAdd > 0) {
lastRow.addView(Space(context), LinearLayout.LayoutParams(0, 0, 1f))
spacersToAdd--
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
index d33cd94004fd..124df32af5e0 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -56,20 +56,19 @@ data class RenderInfo(
enabled: Boolean,
offset: Int = 0
): RenderInfo {
- val (fg, bg) = deviceColorMap.getValue(deviceType)
-
- val iconKey = if (offset > 0) {
+ val key = if (offset > 0) {
deviceType * BUCKET_SIZE + offset
} else deviceType
- val iconState = deviceIconMap.getValue(iconKey)
+ val (fg, bg) = deviceColorMap.getValue(key)
+ val iconState = deviceIconMap.getValue(key)
val resourceId = iconState[enabled]
var icon: Drawable?
if (resourceId == APP_ICON_ID) {
icon = appIconMap.get(componentName)
if (icon == null) {
icon = context.resources
- .getDrawable(R.drawable.ic_device_unknown_gm2_24px, null)
+ .getDrawable(R.drawable.ic_device_unknown_on, null)
appIconMap.put(componentName, icon)
}
} else {
@@ -268,10 +267,74 @@ private val deviceIconMap = mapOf<Int, IconState>(
DeviceTypes.TYPE_ROUTINE to IconState(
RenderInfo.APP_ICON_ID,
RenderInfo.APP_ICON_ID
+ ),
+ DeviceTypes.TYPE_AC_HEATER to IconState(
+ R.drawable.ic_device_thermostat_off,
+ R.drawable.ic_device_thermostat_on
+ ),
+ DeviceTypes.TYPE_AC_UNIT to IconState(
+ R.drawable.ic_device_thermostat_off,
+ R.drawable.ic_device_thermostat_on
+ ),
+ DeviceTypes.TYPE_COFFEE_MAKER to IconState(
+ R.drawable.ic_device_kettle_off,
+ R.drawable.ic_device_kettle_on
+ ),
+ DeviceTypes.TYPE_DEHUMIDIFIER to IconState(
+ R.drawable.ic_device_air_freshener_off,
+ R.drawable.ic_device_air_freshener_on
+ ),
+ DeviceTypes.TYPE_RADIATOR to IconState(
+ R.drawable.ic_device_thermostat_off,
+ R.drawable.ic_device_thermostat_on
+ ),
+ DeviceTypes.TYPE_STANDMIXER to IconState(
+ R.drawable.ic_device_cooking_off,
+ R.drawable.ic_device_cooking_on
+ ),
+ DeviceTypes.TYPE_DISPLAY to IconState(
+ R.drawable.ic_device_display_off,
+ R.drawable.ic_device_display_on
+ ),
+ DeviceTypes.TYPE_DRYER to IconState(
+ R.drawable.ic_device_washer_off,
+ R.drawable.ic_device_washer_on
+ ),
+ DeviceTypes.TYPE_MOWER to IconState(
+ R.drawable.ic_device_outdoor_garden_off,
+ R.drawable.ic_device_outdoor_garden_on
+ ),
+ DeviceTypes.TYPE_SHOWER to IconState(
+ R.drawable.ic_device_water_off,
+ R.drawable.ic_device_water_on
+ ),
+ DeviceTypes.TYPE_AWNING to IconState(
+ R.drawable.ic_device_pergola_off,
+ R.drawable.ic_device_pergola_on
+ ),
+ DeviceTypes.TYPE_CLOSET to IconState(
+ R.drawable.ic_device_drawer_off,
+ R.drawable.ic_device_drawer_on
+ ),
+ DeviceTypes.TYPE_CURTAIN to IconState(
+ R.drawable.ic_device_blinds_off,
+ R.drawable.ic_device_blinds_on
+ ),
+ DeviceTypes.TYPE_DOOR to IconState(
+ R.drawable.ic_device_door_off,
+ R.drawable.ic_device_door_on
+ ),
+ DeviceTypes.TYPE_SHUTTER to IconState(
+ R.drawable.ic_device_window_off,
+ R.drawable.ic_device_window_on
+ ),
+ DeviceTypes.TYPE_HEATER to IconState(
+ R.drawable.ic_device_thermostat_off,
+ R.drawable.ic_device_thermostat_on
)
).withDefault {
IconState(
- R.drawable.ic_device_unknown_gm2_24px,
- R.drawable.ic_device_unknown_gm2_24px
+ R.drawable.ic_device_unknown_off,
+ R.drawable.ic_device_unknown_on
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
index 5b3d5c565472..82ccb17a52c6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
@@ -74,6 +74,8 @@ import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerServiceImpl;
+import com.android.systemui.util.RingerModeTracker;
+import com.android.systemui.util.RingerModeTrackerImpl;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.volume.VolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogControllerImpl;
@@ -264,4 +266,10 @@ public abstract class DependencyBinder {
@Binds
public abstract VolumeComponent provideVolumeComponent(
VolumeDialogComponent volumeDialogComponent);
+
+ /**
+ */
+ @Binds
+ public abstract RingerModeTracker provideRingerModeTracker(
+ RingerModeTrackerImpl ringerModeTrackerImpl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 4dd5e87d2c93..a4a589481b33 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -39,9 +39,11 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.UserInfo;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Color;
+import android.graphics.Insets;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.ConnectivityManager;
@@ -70,6 +72,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
@@ -77,6 +80,11 @@ import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
@@ -113,6 +121,7 @@ import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.EmergencyDialerConstants;
+import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.leak.RotationUtils;
import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
@@ -129,7 +138,8 @@ import javax.inject.Inject;
public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
DialogInterface.OnShowListener,
ConfigurationController.ConfigurationListener,
- GlobalActionsPanelPlugin.Callbacks {
+ GlobalActionsPanelPlugin.Callbacks,
+ LifecycleOwner {
public static final String SYSTEM_DIALOG_REASON_KEY = "reason";
public static final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
@@ -178,6 +188,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final NotificationShadeDepthController mDepthController;
private final BlurUtils mBlurUtils;
+ // Used for RingerModeTracker
+ private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+
private ArrayList<Action> mItems;
private ActionsDialog mDialog;
@@ -188,7 +201,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private boolean mKeyguardShowing = false;
private boolean mDeviceProvisioned = false;
- private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
+ private ToggleState mAirplaneState = ToggleState.Off;
private boolean mIsWaitingForEcmExit = false;
private boolean mHasTelephony;
private boolean mHasVibrator;
@@ -205,7 +218,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final IWindowManager mIWindowManager;
private final Executor mBackgroundExecutor;
private final ControlsListingController mControlsListingController;
- private boolean mAnyControlsProviders = false;
+ private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>();
+ private ControlsController mControlsController;
+ private SharedPreferences mControlsPreferences;
+ private final RingerModeTracker mRingerModeTracker;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -244,7 +260,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
ControlsUiController controlsUiController, IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
ControlsListingController controlsListingController,
- ControlsController controlsController, UiEventLogger uiEventLogger) {
+ ControlsController controlsController, UiEventLogger uiEventLogger,
+ RingerModeTracker ringerModeTracker) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -271,6 +288,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mBackgroundExecutor = backgroundExecutor;
mControlsListingController = controlsListingController;
mBlurUtils = blurUtils;
+ mRingerModeTracker = ringerModeTracker;
+ mControlsController = controlsController;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -290,6 +309,11 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mShowSilentToggle = SHOW_SILENT_TOGGLE && !resources.getBoolean(
R.bool.config_useFixedVolume);
+ if (mShowSilentToggle) {
+ mRingerModeTracker.getRingerMode().observe(this, ringer ->
+ mHandler.sendEmptyMessage(MESSAGE_REFRESH)
+ );
+ }
mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
mScreenshotHelper = new ScreenshotHelper(context);
@@ -309,45 +333,54 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
});
- String preferredControlsPackage = mContext.getResources()
- .getString(com.android.systemui.R.string.config_controlsPreferredPackage);
mControlsListingController.addCallback(list -> {
- mAnyControlsProviders = !list.isEmpty();
-
- /*
- * See if any service providers match the preferred component. If they do,
- * and there are no current favorites, and we haven't successfully loaded favorites to
- * date, query the preferred component for a limited number of suggested controls.
- */
- ComponentName preferredComponent = null;
- for (ControlsServiceInfo info : list) {
- if (info.componentName.getPackageName().equals(preferredControlsPackage)) {
- preferredComponent = info.componentName;
- break;
- }
- }
-
- if (preferredComponent == null) return;
-
- SharedPreferences prefs = context.getSharedPreferences(PREFS_CONTROLS_FILE,
- Context.MODE_PRIVATE);
- boolean isSeeded = prefs.getBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, false);
- boolean hasFavorites = controlsController.getFavorites().size() > 0;
- if (!isSeeded && !hasFavorites) {
- controlsController.seedFavoritesForComponent(
- preferredComponent,
- (accepted) -> {
- Log.i(TAG, "Controls seeded: " + accepted);
- prefs.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED,
- accepted).apply();
- }
- );
- }
+ mControlsServiceInfos = list;
});
+
+ // Need to be user-specific with the context to make sure we read the correct prefs
+ Context userContext = context.createContextAsUser(
+ new UserHandle(mUserManager.getUserHandle()), 0);
+ mControlsPreferences = userContext.getSharedPreferences(PREFS_CONTROLS_FILE,
+ Context.MODE_PRIVATE);
+
}
+ private void seedFavorites() {
+ if (mControlsServiceInfos.isEmpty()
+ || mControlsController.getFavorites().size() > 0
+ || mControlsPreferences.getBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, false)) {
+ return;
+ }
+ /*
+ * See if any service providers match the preferred component. If they do,
+ * and there are no current favorites, and we haven't successfully loaded favorites to
+ * date, query the preferred component for a limited number of suggested controls.
+ */
+ String preferredControlsPackage = mContext.getResources()
+ .getString(com.android.systemui.R.string.config_controlsPreferredPackage);
+ ComponentName preferredComponent = null;
+ for (ControlsServiceInfo info : mControlsServiceInfos) {
+ if (info.componentName.getPackageName().equals(preferredControlsPackage)) {
+ preferredComponent = info.componentName;
+ break;
+ }
+ }
+
+ if (preferredComponent == null) {
+ Log.i(TAG, "Controls seeding: No preferred component has been set, will not seed");
+ mControlsPreferences.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, true).apply();
+ }
+
+ mControlsController.seedFavoritesForComponent(
+ preferredComponent,
+ (accepted) -> {
+ Log.i(TAG, "Controls seeded: " + accepted);
+ mControlsPreferences.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED,
+ accepted).apply();
+ });
+ }
/**
* Show the global actions dialog (creating if necessary)
@@ -393,6 +426,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
awakenIfNecessary();
mDialog = createDialog();
prepareDialog();
+ seedFavorites();
// If we only have 1 item and it's a simple press action, just do this action.
if (mAdapter.getCount() == 1
@@ -594,7 +628,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
@Override
public boolean shouldBeSeparated() {
- return true;
+ return !shouldShowControls();
}
@Override
@@ -602,7 +636,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
View v = super.create(context, convertView, parent, inflater);
int textColor;
- if (shouldBeSeparated()) {
+ if (shouldShowControls()) {
+ v.setBackgroundTintList(ColorStateList.valueOf(v.getResources().getColor(
+ com.android.systemui.R.color.global_actions_emergency_background)));
+ textColor = v.getResources().getColor(
+ com.android.systemui.R.color.global_actions_emergency_text);
+ } else if (shouldBeSeparated()) {
textColor = v.getResources().getColor(
com.android.systemui.R.color.global_actions_alert_text);
} else {
@@ -612,7 +651,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
TextView messageView = v.findViewById(R.id.message);
messageView.setTextColor(textColor);
messageView.setSelected(true); // necessary for marquee to work
- ImageView icon = (ImageView) v.findViewById(R.id.icon);
+ ImageView icon = v.findViewById(R.id.icon);
icon.getDrawable().setTint(textColor);
return v;
}
@@ -982,18 +1021,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
refreshSilentMode();
mAirplaneModeOn.updateState(mAirplaneState);
mAdapter.notifyDataSetChanged();
- if (mShowSilentToggle) {
- IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
- mBroadcastDispatcher.registerReceiver(mRingerModeReceiver, filter);
- }
+ mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
}
private void refreshSilentMode() {
if (!mHasVibrator) {
- final boolean silentModeOn =
- mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+ Integer value = mRingerModeTracker.getRingerMode().getValue();
+ final boolean silentModeOn = value != null && value != AudioManager.RINGER_MODE_NORMAL;
((ToggleAction) mSilentModeAction).updateState(
- silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
+ silentModeOn ? ToggleState.On : ToggleState.Off);
}
}
@@ -1005,14 +1041,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mDialog = null;
}
mWindowManagerFuncs.onGlobalActionsHidden();
- if (mShowSilentToggle) {
- try {
- mBroadcastDispatcher.unregisterReceiver(mRingerModeReceiver);
- } catch (IllegalArgumentException ie) {
- // ignore this
- Log.w(TAG, ie);
- }
- }
+ mLifecycle.setCurrentState(Lifecycle.State.DESTROYED);
}
/**
@@ -1023,6 +1052,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mUiEventLogger.log(GlobalActionsEvent.GA_POWER_MENU_OPEN);
}
+ private int getActionLayoutId() {
+ if (shouldShowControls()) {
+ return com.android.systemui.R.layout.global_actions_grid_item_v2;
+ }
+ return com.android.systemui.R.layout.global_actions_grid_item;
+ }
+
/**
* The adapter used for the list within the global actions dialog, taking into account whether
* the keyguard is showing via
@@ -1234,20 +1270,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
- protected int getActionLayoutId(Context context) {
- if (shouldShowControls()) {
- return com.android.systemui.R.layout.global_actions_grid_item_v2;
- }
- return com.android.systemui.R.layout.global_actions_grid_item;
- }
-
public View create(
Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
- View v = inflater.inflate(getActionLayoutId(context), parent,
- false);
+ View v = inflater.inflate(getActionLayoutId(), parent, false /* attach */);
- ImageView icon = (ImageView) v.findViewById(R.id.icon);
- TextView messageView = (TextView) v.findViewById(R.id.message);
+ ImageView icon = v.findViewById(R.id.icon);
+ TextView messageView = v.findViewById(R.id.message);
messageView.setSelected(true); // necessary for marquee to work
if (mIcon != null) {
@@ -1266,30 +1294,30 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
- /**
- * A toggle action knows whether it is on or off, and displays an icon and status message
- * accordingly.
- */
- private static abstract class ToggleAction implements Action {
-
- enum State {
- Off(false),
- TurningOn(true),
- TurningOff(true),
- On(false);
+ private enum ToggleState {
+ Off(false),
+ TurningOn(true),
+ TurningOff(true),
+ On(false);
- private final boolean inTransition;
+ private final boolean mInTransition;
- State(boolean intermediate) {
- inTransition = intermediate;
- }
+ ToggleState(boolean intermediate) {
+ mInTransition = intermediate;
+ }
- public boolean inTransition() {
- return inTransition;
- }
+ public boolean inTransition() {
+ return mInTransition;
}
+ }
- protected State mState = State.Off;
+ /**
+ * A toggle action knows whether it is on or off, and displays an icon and status message
+ * accordingly.
+ */
+ private abstract class ToggleAction implements Action {
+
+ protected ToggleState mState = ToggleState.Off;
// prefs
protected int mEnabledIconResId;
@@ -1333,13 +1361,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
LayoutInflater inflater) {
willCreate();
- View v = inflater.inflate(com.android.systemui.R
- .layout.global_actions_grid_item, parent, false);
+ View v = inflater.inflate(getActionLayoutId(), parent, false /* attach */);
ImageView icon = (ImageView) v.findViewById(R.id.icon);
TextView messageView = (TextView) v.findViewById(R.id.message);
final boolean enabled = isEnabled();
- boolean on = ((mState == State.On) || (mState == State.TurningOn));
+ boolean on = ((mState == ToggleState.On) || (mState == ToggleState.TurningOn));
if (messageView != null) {
messageView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
@@ -1364,7 +1391,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return;
}
- final boolean nowOn = !(mState == State.On);
+ final boolean nowOn = !(mState == ToggleState.On);
onToggle(nowOn);
changeStateFromPress(nowOn);
}
@@ -1381,12 +1408,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
* @param buttonOn Whether the button was turned on or off
*/
protected void changeStateFromPress(boolean buttonOn) {
- mState = buttonOn ? State.On : State.Off;
+ mState = buttonOn ? ToggleState.On : ToggleState.Off;
}
abstract void onToggle(boolean on);
- public void updateState(State state) {
+ public void updateState(ToggleState state) {
mState = state;
}
}
@@ -1420,7 +1447,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
// In ECM mode airplane state cannot be changed
if (!TelephonyProperties.in_ecm_mode().orElse(false)) {
- mState = buttonOn ? State.TurningOn : State.TurningOff;
+ mState = buttonOn ? ToggleState.TurningOn : ToggleState.TurningOff;
mAirplaneState = mState;
}
}
@@ -1555,21 +1582,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
public void onServiceStateChanged(ServiceState serviceState) {
if (!mHasTelephony) return;
final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
- mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
+ mAirplaneState = inAirplaneMode ? ToggleState.On : ToggleState.Off;
mAirplaneModeOn.updateState(mAirplaneState);
mAdapter.notifyDataSetChanged();
}
};
- private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
- mHandler.sendEmptyMessage(MESSAGE_REFRESH);
- }
- }
- };
-
private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
@@ -1614,7 +1632,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mContentResolver,
Settings.Global.AIRPLANE_MODE_ON,
0) == 1;
- mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
+ mAirplaneState = airplaneModeOn ? ToggleState.On : ToggleState.Off;
mAirplaneModeOn.updateState(mAirplaneState);
}
@@ -1631,10 +1649,16 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
intent.putExtra("state", on);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
if (!mHasTelephony) {
- mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
+ mAirplaneState = on ? ToggleState.On : ToggleState.Off;
}
}
+ @NonNull
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycle;
+ }
+
private static final class ActionsDialog extends Dialog implements DialogInterface,
ColorExtractor.OnColorsChangedListener {
@@ -1888,6 +1912,14 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mGlobalActionsLayout);
})
.start();
+ ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
+ root.setOnApplyWindowInsetsListener((v, windowInsets) -> {
+ if (mControlsUiController != null) {
+ Insets insets = windowInsets.getInsets(WindowInsets.Type.all());
+ root.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ }
+ return WindowInsets.CONSUMED;
+ });
if (mControlsUiController != null) {
mControlsUiController.show(mControlsView);
}
@@ -2017,6 +2049,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private boolean shouldShowControls() {
return mKeyguardStateController.isUnlocked()
&& mControlsUiController.getAvailable()
- && mAnyControlsProviders;
+ && !mControlsServiceInfos.isEmpty();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index aa5ebaa22f2d..b7658a9f178d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -46,19 +46,6 @@ class SeekBarObserver(view: View) : Observer<SeekBarViewModel.Progress> {
/** Updates seek bar views when the data model changes. */
@UiThread
override fun onChanged(data: SeekBarViewModel.Progress) {
- if (data.enabled && seekBarView.visibility == View.GONE) {
- seekBarView.visibility = View.VISIBLE
- elapsedTimeView.visibility = View.VISIBLE
- totalTimeView.visibility = View.VISIBLE
- } else if (!data.enabled && seekBarView.visibility == View.VISIBLE) {
- seekBarView.visibility = View.GONE
- elapsedTimeView.visibility = View.GONE
- totalTimeView.visibility = View.GONE
- return
- }
-
- // TODO: update the style of the disabled progress bar
- seekBarView.setEnabled(data.seekAvailable)
data.color?.let {
var tintList = ColorStateList.valueOf(it)
@@ -71,6 +58,17 @@ class SeekBarObserver(view: View) : Observer<SeekBarViewModel.Progress> {
totalTimeView.setTextColor(it)
}
+ if (!data.enabled) {
+ seekBarView.setEnabled(false)
+ seekBarView.getThumb().setAlpha(0)
+ elapsedTimeView.setText("")
+ totalTimeView.setText("")
+ return
+ }
+
+ seekBarView.getThumb().setAlpha(if (data.seekAvailable) 255 else 0)
+ seekBarView.setEnabled(data.seekAvailable)
+
data.elapsedTime?.let {
seekBarView.setProgress(it)
elapsedTimeView.setText(DateUtils.formatElapsedTime(
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index b10dd93fa695..d2994aebeb33 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -103,6 +103,7 @@ public class PipTaskOrganizer extends TaskOrganizer {
@Override
public void onPipAnimationEnd(SurfaceControl.Transaction tx,
PipAnimationController.PipTransitionAnimator animator) {
+ finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection());
mMainHandler.post(() -> {
for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
@@ -110,7 +111,6 @@ public class PipTaskOrganizer extends TaskOrganizer {
animator.getTransitionDirection());
}
});
- finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index a192afceddb9..a8a5d896537f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -16,12 +16,10 @@
package com.android.systemui.pip.phone;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManager.StackInfo;
import android.app.IActivityTaskManager;
import android.content.Context;
import android.graphics.Rect;
@@ -169,15 +167,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
*/
void synchronizePinnedStackBounds() {
cancelAnimations();
- try {
- StackInfo stackInfo = mActivityTaskManager.getStackInfo(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
- if (stackInfo != null) {
- mBounds.set(stackInfo.bounds);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get pinned stack bounds");
- }
+ mBounds.set(mPipTaskOrganizer.getLastReportedBounds());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index bbb493966533..1e9daab4a0b6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -510,7 +510,7 @@ public class PipTouchHandler {
mHandler.removeCallbacks(mShowTargetAction);
if (mTargetViewContainer.isAttachedToWindow()) {
- mWindowManager.removeView(mTargetViewContainer);
+ mWindowManager.removeViewImmediate(mTargetViewContainer);
}
}
@@ -592,8 +592,14 @@ public class PipTouchHandler {
break;
}
case MotionEvent.ACTION_HOVER_ENTER:
- mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
- mMovementBounds, false /* allowMenuTimeout */, false /* willResizeMenu */);
+ // If Touch Exploration is enabled, some a11y services (e.g. Talkback) is probably
+ // on and changing MotionEvents into HoverEvents.
+ // Let's not enable menu show/hide for a11y services.
+ if (!mAccessibilityManager.isTouchExplorationEnabled()) {
+ mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
+ mMovementBounds, false /* allowMenuTimeout */,
+ false /* willResizeMenu */);
+ }
case MotionEvent.ACTION_HOVER_MOVE: {
if (!shouldDeliverToMenu && !mSendingHoverAccessibilityEvents) {
sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
@@ -602,7 +608,12 @@ public class PipTouchHandler {
break;
}
case MotionEvent.ACTION_HOVER_EXIT: {
- mMenuController.hideMenu();
+ // If Touch Exploration is enabled, some a11y services (e.g. Talkback) is probably
+ // on and changing MotionEvents into HoverEvents.
+ // Let's not enable menu show/hide for a11y services.
+ if (!mAccessibilityManager.isTouchExplorationEnabled()) {
+ mMenuController.hideMenu();
+ }
if (!shouldDeliverToMenu && mSendingHoverAccessibilityEvents) {
sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
mSendingHoverAccessibilityEvents = false;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 52c8960d1ccf..99a01d3f6a7f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -289,7 +289,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
mMediaSessionManager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
- mPipTaskOrganizer.registerPipTransitionCallback(this);
try {
WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
@@ -334,6 +333,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
* Shows the picture-in-picture menu if an activity is in picture-in-picture mode.
*/
public void showPictureInPictureMenu() {
+ if (DEBUG) Log.d(TAG, "showPictureInPictureMenu(), current state=" + getStateDescription());
+
if (getState() == STATE_PIP) {
resizePinnedStack(STATE_PIP_MENU);
}
@@ -343,10 +344,18 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
* Closes PIP (PIPed activity and PIP system UI).
*/
public void closePip() {
+ if (DEBUG) Log.d(TAG, "closePip(), current state=" + getStateDescription());
+
closePipInternal(true);
}
private void closePipInternal(boolean removePipStack) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "closePipInternal() removePipStack=" + removePipStack + ", current state="
+ + getStateDescription());
+ }
+
mState = STATE_NO_PIP;
mPipTaskId = TASK_ID_NO_PIP;
mPipMediaController = null;
@@ -371,6 +380,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
* Moves the PIPed activity to the fullscreen and closes PIP system UI.
*/
void movePipToFullscreen() {
+ if (DEBUG) Log.d(TAG, "movePipToFullscreen(), current state=" + getStateDescription());
+
mPipTaskId = TASK_ID_NO_PIP;
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onMoveToFullscreen();
@@ -386,6 +397,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
public void suspendPipResizing(int reason) {
if (DEBUG) Log.d(TAG,
"suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
+
mSuspendPipResizingReason |= reason;
}
@@ -408,7 +420,11 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
* @param state In Pip state also used to determine the new size for the Pip.
*/
void resizePinnedStack(int state) {
- if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state, new Exception());
+ if (DEBUG) {
+ Log.d(TAG, "resizePinnedStack() state=" + stateToName(state) + ", current state="
+ + getStateDescription(), new Exception());
+ }
+
boolean wasStateNoPip = (mState == STATE_NO_PIP);
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onPipResizeAboutToStart();
@@ -418,7 +434,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
if (DEBUG) Log.d(TAG, "resizePinnedStack() deferring"
+ " mSuspendPipResizingReason=" + mSuspendPipResizingReason
+ " mResumeResizePinnedStackRunnableState="
- + mResumeResizePinnedStackRunnableState);
+ + stateToName(mResumeResizePinnedStackRunnableState));
return;
}
mState = state;
@@ -458,7 +474,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
* stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}.
*/
private void showPipMenu() {
- if (DEBUG) Log.d(TAG, "showPipMenu()");
+ if (DEBUG) Log.d(TAG, "showPipMenu(), current state=" + getStateDescription());
+
mState = STATE_PIP_MENU;
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onShowPipMenu();
@@ -712,6 +729,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
private void onPipTransitionFinishedOrCanceled() {
if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()");
+
if (getState() == STATE_PIP_MENU) {
showPipMenu();
}
@@ -753,4 +771,28 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
WindowManagerWrapper.getInstance().setPipVisibility(visible);
});
}
+
+ private String getStateDescription() {
+ if (mSuspendPipResizingReason == 0) {
+ return stateToName(mState);
+ }
+ return stateToName(mResumeResizePinnedStackRunnableState) + " (while " + stateToName(mState)
+ + " is suspended)";
+ }
+
+ private static String stateToName(int state) {
+ switch (state) {
+ case STATE_NO_PIP:
+ return "NO_PIP";
+
+ case STATE_PIP:
+ return "PIP";
+
+ case STATE_PIP_MENU:
+ return "PIP_MENU";
+
+ default:
+ return "UNKNOWN(" + state + ")";
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index f43f8e795fe6..c7e77ccfa488 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -22,6 +22,7 @@ import android.app.Activity;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.os.Bundle;
+import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.pip.tv.dagger.TvPipComponent;
@@ -34,6 +35,7 @@ import javax.inject.Inject;
* Activity to show the PIP menu to control PIP.
*/
public class PipMenuActivity extends Activity implements PipManager.Listener {
+ private static final boolean DEBUG = false;
private static final String TAG = "PipMenuActivity";
static final String EXTRA_CUSTOM_ACTIONS = "custom_actions";
@@ -47,7 +49,6 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
private boolean mRestorePipSizeWhenClose;
private PipControlsViewController mPipControlsViewController;
-
@Inject
public PipMenuActivity(TvPipComponent.Builder pipComponentBuilder, PipManager pipManager) {
super();
@@ -57,6 +58,8 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
@Override
protected void onCreate(Bundle bundle) {
+ if (DEBUG) Log.d(TAG, "onCreate()");
+
super.onCreate(bundle);
if (!mPipManager.isPipShown()) {
finish();
@@ -81,13 +84,18 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
@Override
protected void onNewIntent(Intent intent) {
+ if (DEBUG) Log.d(TAG, "onNewIntent(), intent=" + intent);
super.onNewIntent(intent);
onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS));
}
private void restorePipAndFinish() {
+ if (DEBUG) Log.d(TAG, "restorePipAndFinish()");
+
if (mRestorePipSizeWhenClose) {
+ if (DEBUG) Log.d(TAG, " > restoring to the default position");
+
// When PIP menu activity is closed, restore to the default position.
mPipManager.resizePinnedStack(PipManager.STATE_PIP);
}
@@ -96,12 +104,16 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
@Override
public void onResume() {
+ if (DEBUG) Log.d(TAG, "onResume()");
+
super.onResume();
mFadeInAnimation.start();
}
@Override
public void onPause() {
+ if (DEBUG) Log.d(TAG, "onPause()");
+
super.onPause();
mFadeOutAnimation.start();
restorePipAndFinish();
@@ -109,6 +121,8 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
@Override
protected void onDestroy() {
+ if (DEBUG) Log.d(TAG, "onDestroy()");
+
super.onDestroy();
mPipManager.removeListener(this);
mPipManager.resumePipResizing(
@@ -117,29 +131,41 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
@Override
public void onBackPressed() {
+ if (DEBUG) Log.d(TAG, "onBackPressed()");
+
restorePipAndFinish();
}
@Override
- public void onPipEntered() { }
+ public void onPipEntered() {
+ if (DEBUG) Log.d(TAG, "onPipEntered()");
+ }
@Override
public void onPipActivityClosed() {
+ if (DEBUG) Log.d(TAG, "onPipActivityClosed()");
+
finish();
}
@Override
public void onPipMenuActionsChanged(ParceledListSlice actions) {
+ if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()");
+
boolean hasCustomActions = actions != null && !actions.getList().isEmpty();
mPipControlsViewController.setActions(
hasCustomActions ? actions.getList() : Collections.EMPTY_LIST);
}
@Override
- public void onShowPipMenu() { }
+ public void onShowPipMenu() {
+ if (DEBUG) Log.d(TAG, "onShowPipMenu()");
+ }
@Override
public void onMoveToFullscreen() {
+ if (DEBUG) Log.d(TAG, "onMoveToFullscreen()");
+
// Moving PIP to fullscreen is implemented by resizing PINNED_STACK with null bounds.
// This conflicts with restoring PIP position, so disable it.
mRestorePipSizeWhenClose = false;
@@ -148,8 +174,17 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
@Override
public void onPipResizeAboutToStart() {
+ if (DEBUG) Log.d(TAG, "onPipResizeAboutToStart()");
+
finish();
mPipManager.suspendPipResizing(
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
}
+
+ @Override
+ public void finish() {
+ if (DEBUG) Log.d(TAG, "finish()", new RuntimeException());
+
+ super.finish();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index e1ffad4df2c3..90dc38f3ad12 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -22,10 +22,8 @@ import static com.android.systemui.util.Utils.useQsMediaPlayer;
import android.annotation.ColorInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -49,13 +47,16 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DualToneHandler;
import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
@@ -70,6 +71,7 @@ import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.DateView;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.RingerModeTracker;
import java.util.ArrayList;
import java.util.List;
@@ -86,7 +88,7 @@ import javax.inject.Named;
*/
public class QuickStatusBarHeader extends RelativeLayout implements
View.OnClickListener, NextAlarmController.NextAlarmChangeCallback,
- ZenModeController.Callback {
+ ZenModeController.Callback, LifecycleOwner {
private static final String TAG = "QuickStatusBarHeader";
private static final boolean DEBUG = false;
@@ -137,15 +139,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private DateView mDateView;
private BatteryMeterView mBatteryRemainingIcon;
- private BroadcastDispatcher mBroadcastDispatcher;
+ // Used for RingerModeTracker
+ private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
- private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mRingerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
- updateStatusText();
- }
- };
private boolean mHasTopCutout = false;
private int mRoundedCornerPadding = 0;
@@ -154,7 +150,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
NextAlarmController nextAlarmController, ZenModeController zenModeController,
StatusBarIconController statusBarIconController,
ActivityStarter activityStarter,
- CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher) {
+ CommandQueue commandQueue, RingerModeTracker ringerModeTracker) {
super(context, attrs);
mAlarmController = nextAlarmController;
mZenController = zenModeController;
@@ -162,8 +158,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mActivityStarter = activityStarter;
mDualToneHandler = new DualToneHandler(
new ContextThemeWrapper(context, R.style.QSHeaderTheme));
- mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
+ ringerModeTracker.getRingerModeInternal().observe(this, ringer -> {
+ mRingerMode = ringer;
+ updateStatusText();
+ });
}
@Override
@@ -484,12 +483,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements
if (listening) {
mZenController.addCallback(this);
mAlarmController.addCallback(this);
- mBroadcastDispatcher.registerReceiver(mRingerReceiver,
- new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
+ mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
} else {
mZenController.removeCallback(this);
mAlarmController.removeCallback(this);
- mBroadcastDispatcher.unregisterReceiver(mRingerReceiver);
+ mLifecycle.setCurrentState(Lifecycle.State.DESTROYED);
}
}
@@ -586,4 +584,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements
lp.rightMargin = sideMargins;
}
}
+
+ @NonNull
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycle;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 295adae9c9ee..9324b14bf211 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,6 +24,7 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -555,8 +556,8 @@ public class NotificationEntryManager implements
NotificationEntry entry = new NotificationEntry(
notification,
ranking,
- mFgsFeatureController.isForegroundServiceDismissalEnabled());
- mAllNotifications.add(entry);
+ mFgsFeatureController.isForegroundServiceDismissalEnabled(),
+ SystemClock.uptimeMillis());
mLeakDetector.trackInstance(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 9c2cac71925e..365862bef871 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -47,6 +47,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Notification;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
@@ -387,7 +388,7 @@ public class NotifCollection implements Dumpable {
if (entry == null) {
// A new notification!
- entry = new NotificationEntry(sbn, ranking);
+ entry = new NotificationEntry(sbn, ranking, SystemClock.uptimeMillis());
mNotificationSet.put(sbn.getKey(), entry);
mLogger.logNotifPosted(sbn.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index c1ba26d034ed..68ec34e90760 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -35,6 +35,7 @@ import static com.android.systemui.statusbar.notification.stack.NotificationSect
import static java.util.Objects.requireNonNull;
+import android.annotation.CurrentTimeMillisLong;
import android.app.Notification;
import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
@@ -93,6 +94,7 @@ public final class NotificationEntry extends ListEntry {
private final String mKey;
private StatusBarNotification mSbn;
private Ranking mRanking;
+ private long mCreationTime;
/*
* Bookkeeping members
@@ -171,21 +173,29 @@ public final class NotificationEntry extends ListEntry {
private boolean mAllowFgsDismissal;
private int mBucket = BUCKET_ALERTING;
+ /**
+ * @param sbn the StatusBarNotification from system server
+ * @param ranking also from system server
+ * @param creationTime SystemClock.uptimeMillis of when we were created
+ */
public NotificationEntry(
@NonNull StatusBarNotification sbn,
- @NonNull Ranking ranking) {
- this(sbn, ranking, false);
+ @NonNull Ranking ranking,
+ long creationTime) {
+ this(sbn, ranking, false, creationTime);
}
public NotificationEntry(
@NonNull StatusBarNotification sbn,
@NonNull Ranking ranking,
- boolean allowFgsDismissal
+ boolean allowFgsDismissal,
+ long creationTime
) {
super(requireNonNull(Objects.requireNonNull(sbn).getKey()));
requireNonNull(ranking);
+ mCreationTime = creationTime;
mKey = sbn.getKey();
setSbn(sbn);
setRanking(ranking);
@@ -238,6 +248,21 @@ public final class NotificationEntry extends ListEntry {
}
/**
+ * A timestamp of SystemClock.uptimeMillis() of when this entry was first created, regardless
+ * of any changes to the data presented. It is set once on creation and will never change, and
+ * allows us to know exactly how long this notification has been alive for in our listener
+ * service. It is entirely unrelated to the information inside of the notification.
+ *
+ * This is different to Notification#when because it persists throughout updates, whereas
+ * system server treats every single call to notify() as a new notification and we handle
+ * updates to NotificationEntry locally.
+ */
+ @CurrentTimeMillisLong
+ public long getCreationTime() {
+ return mCreationTime;
+ }
+
+ /**
* Should only be called by NotificationEntryManager and friends.
* TODO: Make this package-private
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 5205bab8fea3..7ac066277c86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -169,7 +169,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
super.onContentUpdated(row);
- mIsLowPriority = row.isLowPriority();
+ mIsLowPriority = row.getEntry().isAmbient();
mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 14af466a2424..d4f4d3bfbb54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -39,6 +39,8 @@ import android.telecom.TelecomManager;
import android.text.format.DateFormat;
import android.util.Log;
+import androidx.lifecycle.Observer;
+
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -64,6 +66,7 @@ import com.android.systemui.statusbar.policy.RotationLockController.RotationLock
import com.android.systemui.statusbar.policy.SensorPrivacyController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.time.DateFormatUtil;
import java.util.Locale;
@@ -109,7 +112,6 @@ public class PhoneStatusBarPolicy
private final SharedPreferences mSharedPreferences;
private final DateFormatUtil mDateFormatUtil;
private final TelecomManager mTelecomManager;
- private final AudioManager mAudioManager;
private final Handler mHandler = new Handler();
private final CastController mCast;
@@ -132,6 +134,7 @@ public class PhoneStatusBarPolicy
private final Executor mUiBgExecutor;
private final SensorPrivacyController mSensorPrivacyController;
private final RecordingController mRecordingController;
+ private final RingerModeTracker mRingerModeTracker;
private boolean mZenVisible;
private boolean mVolumeVisible;
@@ -154,10 +157,11 @@ public class PhoneStatusBarPolicy
KeyguardStateController keyguardStateController,
LocationController locationController,
SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager,
- AlarmManager alarmManager, UserManager userManager, AudioManager audioManager,
+ AlarmManager alarmManager, UserManager userManager,
RecordingController recordingController,
@Nullable TelecomManager telecomManager, @DisplayId int displayId,
- @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil) {
+ @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil,
+ RingerModeTracker ringerModeTracker) {
mIconController = iconController;
mCommandQueue = commandQueue;
mBroadcastDispatcher = broadcastDispatcher;
@@ -179,8 +183,8 @@ public class PhoneStatusBarPolicy
mSensorPrivacyController = sensorPrivacyController;
mRecordingController = recordingController;
mUiBgExecutor = uiBgExecutor;
- mAudioManager = audioManager;
mTelecomManager = telecomManager;
+ mRingerModeTracker = ringerModeTracker;
mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast);
mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot);
@@ -208,8 +212,7 @@ public class PhoneStatusBarPolicy
public void init() {
// listen for broadcasts
IntentFilter filter = new IntentFilter();
- filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
- filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
+
filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
@@ -217,6 +220,10 @@ public class PhoneStatusBarPolicy
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
+ Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen);
+
+ mRingerModeTracker.getRingerMode().observeForever(observer);
+ mRingerModeTracker.getRingerModeInternal().observeForever(observer);
// listen for user / profile change.
try {
@@ -350,14 +357,18 @@ public class PhoneStatusBarPolicy
}
if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConsolidatedPolicy())) {
- if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
- volumeVisible = true;
- volumeIconId = R.drawable.stat_sys_ringer_vibrate;
- volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate);
- } else if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
- volumeVisible = true;
- volumeIconId = R.drawable.stat_sys_ringer_silent;
- volumeDescription = mResources.getString(R.string.accessibility_ringer_silent);
+ final Integer ringerModeInternal =
+ mRingerModeTracker.getRingerModeInternal().getValue();
+ if (ringerModeInternal != null) {
+ if (ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
+ volumeVisible = true;
+ volumeIconId = R.drawable.stat_sys_ringer_vibrate;
+ volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate);
+ } else if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) {
+ volumeVisible = true;
+ volumeIconId = R.drawable.stat_sys_ringer_silent;
+ volumeDescription = mResources.getString(R.string.accessibility_ringer_silent);
+ }
}
}
@@ -616,10 +627,6 @@ public class PhoneStatusBarPolicy
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
- case AudioManager.RINGER_MODE_CHANGED_ACTION:
- case AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION:
- updateVolumeZen();
- break;
case Intent.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt b/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
new file mode 100644
index 000000000000..d65b285adb0c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.graphics.PointF
+import android.os.Handler
+import android.view.MotionEvent
+import android.view.VelocityTracker
+import android.view.View
+import android.view.ViewConfiguration
+import kotlin.math.hypot
+
+/**
+ * Listener which receives [onDown], [onMove], and [onUp] events, with relevant information about
+ * the coordinates of the touch and the view relative to the initial ACTION_DOWN event and the
+ * view's initial position.
+ */
+abstract class RelativeTouchListener : View.OnTouchListener {
+
+ /**
+ * Called when an ACTION_DOWN event is received for the given view.
+ *
+ * @return False if the object is not interested in MotionEvents at this time, or true if we
+ * should consume this event and subsequent events, and begin calling [onMove].
+ */
+ abstract fun onDown(v: View, ev: MotionEvent): Boolean
+
+ /**
+ * Called when an ACTION_MOVE event is received for the given view. This signals that the view
+ * is being dragged.
+ *
+ * @param viewInitialX The view's translationX value when this touch gesture started.
+ * @param viewInitialY The view's translationY value when this touch gesture started.
+ * @param dx Horizontal distance covered since the initial ACTION_DOWN event, in pixels.
+ * @param dy Vertical distance covered since the initial ACTION_DOWN event, in pixels.
+ */
+ abstract fun onMove(
+ v: View,
+ ev: MotionEvent,
+ viewInitialX: Float,
+ viewInitialY: Float,
+ dx: Float,
+ dy: Float
+ )
+
+ /**
+ * Called when an ACTION_UP event is received for the given view. This signals that a drag or
+ * fling gesture has completed.
+ *
+ * @param viewInitialX The view's translationX value when this touch gesture started.
+ * @param viewInitialY The view's translationY value when this touch gesture started.
+ * @param dx Horizontal distance covered, in pixels.
+ * @param dy Vertical distance covered, in pixels.
+ * @param velX The final horizontal velocity of the gesture, in pixels/second.
+ * @param velY The final vertical velocity of the gesture, in pixels/second.
+ */
+ abstract fun onUp(
+ v: View,
+ ev: MotionEvent,
+ viewInitialX: Float,
+ viewInitialY: Float,
+ dx: Float,
+ dy: Float,
+ velX: Float,
+ velY: Float
+ )
+
+ /** The raw coordinates of the last ACTION_DOWN event. */
+ private val touchDown = PointF()
+
+ /** The coordinates of the view, at the time of the last ACTION_DOWN event. */
+ private val viewPositionOnTouchDown = PointF()
+
+ private val velocityTracker = VelocityTracker.obtain()
+
+ private var touchSlop: Int = -1
+ private var movedEnough = false
+
+ private val handler = Handler()
+ private var performedLongClick = false
+
+ @Suppress("UNCHECKED_CAST")
+ override fun onTouch(v: View, ev: MotionEvent): Boolean {
+ addMovement(ev)
+
+ val dx = ev.rawX - touchDown.x
+ val dy = ev.rawY - touchDown.y
+
+ when (ev.action) {
+ MotionEvent.ACTION_DOWN -> {
+ if (!onDown(v, ev)) {
+ return false
+ }
+
+ // Grab the touch slop, it might have changed if the config changed since the
+ // last gesture.
+ touchSlop = ViewConfiguration.get(v.context).scaledTouchSlop
+
+ touchDown.set(ev.rawX, ev.rawY)
+ viewPositionOnTouchDown.set(v.translationX, v.translationY)
+
+ performedLongClick = false
+ handler.postDelayed({
+ performedLongClick = v.performLongClick()
+ }, ViewConfiguration.getLongPressTimeout().toLong())
+ }
+
+ MotionEvent.ACTION_MOVE -> {
+ if (!movedEnough && hypot(dx, dy) > touchSlop && !performedLongClick) {
+ movedEnough = true
+ handler.removeCallbacksAndMessages(null)
+ }
+
+ if (movedEnough) {
+ onMove(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y, dx, dy)
+ }
+ }
+
+ MotionEvent.ACTION_UP -> {
+ if (movedEnough) {
+ velocityTracker.computeCurrentVelocity(1000 /* units */)
+ onUp(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y, dx, dy,
+ velocityTracker.xVelocity, velocityTracker.yVelocity)
+ } else if (!performedLongClick) {
+ v.performClick()
+ } else {
+ handler.removeCallbacksAndMessages(null)
+ }
+
+ velocityTracker.clear()
+ movedEnough = false
+ }
+ }
+
+ return true
+ }
+
+ /**
+ * Adds a movement to the velocity tracker using raw screen coordinates.
+ */
+ private fun addMovement(event: MotionEvent) {
+ val deltaX = event.rawX - event.x
+ val deltaY = event.rawY - event.y
+ event.offsetLocation(deltaX, deltaY)
+ velocityTracker.addMovement(event)
+ event.offsetLocation(-deltaX, -deltaY)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt b/packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt
new file mode 100644
index 000000000000..047298e6ed81
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import androidx.lifecycle.LiveData
+
+interface RingerModeTracker {
+
+ val ringerMode: LiveData<Int>
+ val ringerModeInternal: LiveData<Int>
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt
new file mode 100644
index 000000000000..58684c386995
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.media.AudioManager
+import android.os.UserHandle
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Background
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class RingerModeTrackerImpl @Inject constructor(
+ audioManager: AudioManager,
+ broadcastDispatcher: BroadcastDispatcher,
+ @Background executor: Executor
+) : RingerModeTracker {
+
+ override val ringerMode: LiveData<Int> = RingerModeLiveData(
+ broadcastDispatcher,
+ executor,
+ AudioManager.RINGER_MODE_CHANGED_ACTION,
+ audioManager::getRingerMode
+ )
+
+ override val ringerModeInternal: LiveData<Int> = RingerModeLiveData(
+ broadcastDispatcher,
+ executor,
+ AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION,
+ audioManager::getRingerModeInternal
+ )
+}
+
+class RingerModeLiveData(
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val executor: Executor,
+ intent: String,
+ private val getter: () -> Int
+) : MutableLiveData<Int>() {
+
+ private val filter = IntentFilter(intent)
+ var initialSticky: Boolean = false
+ private set
+
+ private val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ initialSticky = isInitialStickyBroadcast
+ postValue(intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1))
+ }
+ }
+
+ override fun getValue(): Int {
+ return super.getValue() ?: -1
+ }
+
+ override fun onActive() {
+ super.onActive()
+ broadcastDispatcher.registerReceiver(receiver, filter, executor, UserHandle.ALL)
+ executor.execute {
+ postValue(getter())
+ }
+ }
+
+ override fun onInactive() {
+ super.onInactive()
+ broadcastDispatcher.unregisterReceiver(receiver)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 73532632c875..f19c49cc123f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -55,6 +55,8 @@ import android.util.Log;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
+import androidx.lifecycle.Observer;
+
import com.android.internal.annotations.GuardedBy;
import com.android.settingslib.volume.MediaSessions;
import com.android.systemui.Dumpable;
@@ -64,6 +66,8 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.RingerModeLiveData;
+import com.android.systemui.util.RingerModeTracker;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -122,6 +126,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
private final NotificationManager mNoMan;
private final SettingObserver mObserver;
private final Receiver mReceiver = new Receiver();
+ private final RingerModeObservers mRingerModeObservers;
private final MediaSessions mMediaSessions;
protected C mCallbacks = new C();
private final State mState = new State();
@@ -145,7 +150,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
@Inject
public VolumeDialogControllerImpl(Context context, BroadcastDispatcher broadcastDispatcher,
- Optional<Lazy<StatusBar>> statusBarOptionalLazy) {
+ Optional<Lazy<StatusBar>> statusBarOptionalLazy, RingerModeTracker ringerModeTracker) {
mContext = context.getApplicationContext();
// TODO(b/150663459): remove this TV workaround once StatusBar is "unbound" on TVs
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
@@ -164,6 +169,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mObserver = new SettingObserver(mWorker);
+ mRingerModeObservers = new RingerModeObservers(
+ (RingerModeLiveData) ringerModeTracker.getRingerMode(),
+ (RingerModeLiveData) ringerModeTracker.getRingerModeInternal()
+ );
+ mRingerModeObservers.init();
mBroadcastDispatcher = broadcastDispatcher;
mObserver.init();
mReceiver.init();
@@ -246,6 +256,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mMediaSessions.destroy();
mObserver.destroy();
mReceiver.destroy();
+ mRingerModeObservers.destroy();
mWorkerThread.quitSafely();
}
@@ -528,7 +539,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
ss.name = STREAMS.get(stream);
checkRoutedToBluetoothW(stream);
}
- updateRingerModeExternalW(mAudio.getRingerMode());
+ // We are not destroyed so this is listening and has updated information
+ updateRingerModeExternalW(mRingerModeObservers.mRingerMode.getValue());
updateZenModeW();
updateZenConfig();
updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
@@ -575,7 +587,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
Events.writeEvent(Events.EVENT_MUTE_CHANGED, stream, muted);
}
if (muted && isRinger(stream)) {
- updateRingerModeInternalW(mAudio.getRingerModeInternal());
+ updateRingerModeInternalW(mRingerModeObservers.mRingerModeInternal.getValue());
}
return true;
}
@@ -964,6 +976,79 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
}
+ private final class RingerModeObservers {
+
+ private final RingerModeLiveData mRingerMode;
+ private final RingerModeLiveData mRingerModeInternal;
+
+ private final Observer<Integer> mRingerModeObserver = new Observer<Integer>() {
+ @Override
+ public void onChanged(Integer value) {
+ mWorker.post(() -> {
+ final int rm = value;
+ if (mRingerMode.getInitialSticky()) {
+ mState.ringerModeExternal = rm;
+ }
+ if (D.BUG) {
+ Log.d(TAG, "onChange ringer_mode rm="
+ + Util.ringerModeToString(rm));
+ }
+ if (updateRingerModeExternalW(rm)) {
+ mCallbacks.onStateChanged(mState);
+ }
+ }
+ );
+ }
+ };
+
+ private final Observer<Integer> mRingerModeInternalObserver = new Observer<Integer>() {
+ @Override
+ public void onChanged(Integer value) {
+ mWorker.post(() -> {
+ final int rm = value;
+ if (mRingerModeInternal.getInitialSticky()) {
+ mState.ringerModeInternal = rm;
+ }
+ if (D.BUG) {
+ Log.d(TAG, "onChange internal_ringer_mode rm="
+ + Util.ringerModeToString(rm));
+ }
+ if (updateRingerModeInternalW(rm)) {
+ mCallbacks.onStateChanged(mState);
+ }
+ }
+ );
+ }
+ };
+
+ RingerModeObservers(RingerModeLiveData ringerMode,
+ RingerModeLiveData ringerModeInternal) {
+ mRingerMode = ringerMode;
+ mRingerModeInternal = ringerModeInternal;
+ }
+
+ public void init() {
+ int initialValue = mRingerMode.getValue();
+ if (initialValue != -1) {
+ // If it's not -1, set it to the initial value, if it's -1, it means that the
+ // tracker is not listening already and will obtain the sticky value.
+ mState.ringerModeExternal = initialValue;
+ }
+ mRingerMode.observeForever(mRingerModeObserver);
+ initialValue = mRingerModeInternal.getValue();
+ if (initialValue != -1) {
+ // If it's not -1, set it to the initial value, if it's -1, it means that the
+ // tracker is not listening already and will obtain the sticky value.
+ mState.ringerModeInternal = initialValue;
+ }
+ mRingerModeInternal.observeForever(mRingerModeInternalObserver);
+ }
+
+ public void destroy() {
+ mRingerMode.removeObserver(mRingerModeObserver);
+ mRingerModeInternal.removeObserver(mRingerModeInternalObserver);
+ }
+ }
private final class SettingObserver extends ContentObserver {
private final Uri ZEN_MODE_URI =
@@ -1006,8 +1091,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
final IntentFilter filter = new IntentFilter();
filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
- filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
- filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
@@ -1042,18 +1125,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
+ stream + " devices=" + devices + " oldDevices=" + oldDevices);
changed = checkRoutedToBluetoothW(stream);
changed |= onVolumeChangedW(stream, 0);
- } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
- final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
- if (isInitialStickyBroadcast()) mState.ringerModeExternal = rm;
- if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm="
- + Util.ringerModeToString(rm));
- changed = updateRingerModeExternalW(rm);
- } else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
- final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
- if (isInitialStickyBroadcast()) mState.ringerModeInternal = rm;
- if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm="
- + Util.ringerModeToString(rm));
- changed = updateRingerModeInternalW(rm);
} else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
final boolean muted = intent
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index cf1299fc66e0..ce032c94deab 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -115,6 +115,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
@Test
public void testShow_dismissedByCallback() throws Exception {
+ doAnswer(answerVoid(Runnable::run)).when(mHandler).post(any(Runnable.class));
doAnswer(invocation -> {
IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1];
callback.onDismiss();
@@ -184,7 +185,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
private void verifyViewDismissed(SurfaceView v) throws Exception {
verify(mParent).removeView(v);
- verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID);
+ verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index d47fceea9724..64590fd3a36b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -25,6 +25,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,6 +37,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
@UiThreadTest
@Test
public void showSecurityScreen_canInflateAllModes() {
+ mDependency.injectMockDependency(KeyguardStateController.class);
KeyguardSecurityContainer keyguardSecurityContainer =
new KeyguardSecurityContainer(getContext());
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 73f9d8ad6953..7403a11fecbf 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -29,6 +29,8 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
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;
@@ -50,6 +52,7 @@ import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
@@ -63,18 +66,24 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
+
import com.android.internal.telephony.TelephonyIntents;
import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.util.RingerModeTracker;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -125,6 +134,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
private Executor mBackgroundExecutor;
+ @Mock
+ private RingerModeTracker mRingerModeTracker;
+ @Mock
+ private LiveData<Integer> mRingerModeLiveData;
private TestableLooper mTestableLooper;
private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -156,6 +169,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
context.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
+ when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
+
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(context);
@@ -613,6 +628,29 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.getSecondaryLockscreenRequirement(user)).isNull();
}
+ @Test
+ public void testRingerModeChange() {
+ ArgumentCaptor<Observer<Integer>> captor = ArgumentCaptor.forClass(Observer.class);
+ verify(mRingerModeLiveData).observeForever(captor.capture());
+ Observer<Integer> observer = captor.getValue();
+
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+
+ mKeyguardUpdateMonitor.registerCallback(callback);
+
+ observer.onChanged(AudioManager.RINGER_MODE_NORMAL);
+ observer.onChanged(AudioManager.RINGER_MODE_SILENT);
+ observer.onChanged(AudioManager.RINGER_MODE_VIBRATE);
+
+ mTestableLooper.processAllMessages();
+
+ InOrder orderVerify = inOrder(callback);
+ orderVerify.verify(callback).onRingerModeChanged(anyInt()); // Initial update on register
+ orderVerify.verify(callback).onRingerModeChanged(AudioManager.RINGER_MODE_NORMAL);
+ orderVerify.verify(callback).onRingerModeChanged(AudioManager.RINGER_MODE_SILENT);
+ orderVerify.verify(callback).onRingerModeChanged(AudioManager.RINGER_MODE_VIBRATE);
+ }
+
private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver) {
BroadcastReceiver.PendingResult pendingResult =
new BroadcastReceiver.PendingResult(Activity.RESULT_OK,
@@ -643,7 +681,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
protected TestableKeyguardUpdateMonitor(Context context) {
super(context,
TestableLooper.get(KeyguardUpdateMonitorTest.this).getLooper(),
- mBroadcastDispatcher, mDumpManager, mBackgroundExecutor);
+ mBroadcastDispatcher, mDumpManager,
+ mRingerModeTracker, mBackgroundExecutor);
mStrongAuthTracker = KeyguardUpdateMonitorTest.this.mStrongAuthTracker;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 69e933e95562..45f9437efc15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -52,6 +52,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
import junit.framework.Assert;
@@ -69,6 +70,7 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
private ForegroundServiceController mFsc;
private ForegroundServiceNotificationListener mListener;
private NotificationEntryListener mEntryListener;
+ private final FakeSystemClock mClock = new FakeSystemClock();
@Mock private NotificationEntryManager mEntryManager;
@Mock private AppOpsController mAppOpsController;
@Mock private Handler mMainHandler;
@@ -80,9 +82,10 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
- mFsc = new ForegroundServiceController(mEntryManager, mAppOpsController, mMainHandler);
+ mFsc = new ForegroundServiceController(
+ mEntryManager, mAppOpsController, mMainHandler);
mListener = new ForegroundServiceNotificationListener(
- mContext, mFsc, mEntryManager, mNotifPipeline);
+ mContext, mFsc, mEntryManager, mNotifPipeline, mClock);
ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
ArgumentCaptor.forClass(NotificationEntryListener.class);
verify(mEntryManager).addNotificationEntryListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
index 46a473bd6543..bca8deeafd34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
@@ -17,7 +17,6 @@
package com.android.systemui;
import static com.android.systemui.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS;
-import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -29,6 +28,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -37,12 +37,15 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ForegroundServiceNotificationListenerTest extends SysuiTestCase {
- private ForegroundServiceLifetimeExtender mExtender = new ForegroundServiceLifetimeExtender();
+ private ForegroundServiceLifetimeExtender mExtender;
private NotificationEntry mEntry;
private Notification mNotif;
+ private final FakeSystemClock mClock = new FakeSystemClock();
@Before
public void setup() {
+ mExtender = new ForegroundServiceLifetimeExtender(mClock);
+
mNotif = new Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
@@ -50,6 +53,7 @@ public class ForegroundServiceNotificationListenerTest extends SysuiTestCase {
.build();
mEntry = new NotificationEntryBuilder()
+ .setCreationTime(mClock.uptimeMillis())
.setNotification(mNotif)
.build();
}
@@ -62,27 +66,26 @@ public class ForegroundServiceNotificationListenerTest extends SysuiTestCase {
// Extend the lifetime of a FGS notification iff it has not been visible
// for the minimum time
mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- modifySbn(mEntry)
- .setPostTime(System.currentTimeMillis())
- .build();
+
+ // No time has elapsed, keep showing
assertTrue(mExtender.shouldExtendLifetime(mEntry));
}
@Test
public void testShouldExtendLifetime_shouldNot_foreground() {
mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- modifySbn(mEntry)
- .setPostTime(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1)
- .build();
+
+ // Entry was created at mClock.uptimeMillis(), advance it MIN_FGS_TIME_MS + 1
+ mClock.advanceTime(MIN_FGS_TIME_MS + 1);
assertFalse(mExtender.shouldExtendLifetime(mEntry));
}
@Test
public void testShouldExtendLifetime_shouldNot_notForeground() {
mNotif.flags = 0;
- modifySbn(mEntry)
- .setPostTime(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1)
- .build();
+
+ // Entry was created at mClock.uptimeMillis(), advance it MIN_FGS_TIME_MS + 1
+ mClock.advanceTime(MIN_FGS_TIME_MS + 1);
assertFalse(mExtender.shouldExtendLifetime(mEntry));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 32604f8609fa..b2c35867e789 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -38,6 +38,8 @@ import com.android.systemui.broadcast.FakeBroadcastDispatcher;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import org.junit.After;
import org.junit.Before;
@@ -97,6 +99,12 @@ public abstract class SysuiTestCase {
// A lot of tests get the LocalBluetoothManager, often via several layers of indirection.
// None of them actually need it.
mDependency.injectMockDependency(LocalBluetoothManager.class);
+
+ // Notifications tests are injecting one of these, causing many classes (including
+ // KeyguardUpdateMonitor to be created (injected).
+ // TODO(b/1531701009) Clean up NotificationContentView creation to prevent this
+ mDependency.injectMockDependency(SmartReplyController.class);
+ mDependency.injectMockDependency(NotificationBlockingHelperManager.class);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 037f04ec1d7c..e472de349466 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -374,7 +374,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
- mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
assertFalse(mNotificationShadeWindowController.getBubblesShowing());
verify(mNotificationEntryManager, times(3)).updateNotifications(any());
assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
@@ -399,7 +399,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
assertTrue(mNotificationShadeWindowController.getBubbleExpanded());
@@ -436,7 +436,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
@@ -448,7 +448,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mRow2.getEntry()));
// Switch which bubble is expanded
- mBubbleController.selectBubble(mRow.getEntry().getKey());
+ mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
mBubbleData.setExpanded(true);
assertEquals(mRow.getEntry(),
mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
@@ -482,7 +482,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
// Expand
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
@@ -510,7 +510,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
// Expand
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
@@ -544,7 +544,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mSysUiStateBubblesExpanded);
@@ -726,7 +726,7 @@ public class BubbleControllerTest extends SysuiTestCase {
public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
- mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(2)).send();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 545de210d5b5..5f4f2ef04c1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -321,7 +321,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
- mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
assertFalse(mNotificationShadeWindowController.getBubblesShowing());
verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
@@ -344,7 +344,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
assertTrue(mNotificationShadeWindowController.getBubbleExpanded());
@@ -376,7 +376,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
@@ -385,7 +385,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry()));
// Switch which bubble is expanded
- mBubbleController.selectBubble(mRow.getEntry().getKey());
+ mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
mBubbleData.setExpanded(true);
assertEquals(mRow.getEntry(),
mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
@@ -416,7 +416,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
// Expand
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
@@ -442,7 +442,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
// Expand
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
@@ -474,7 +474,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
@@ -628,7 +628,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
- mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(2)).send();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index 88316f2d4323..bb003ee59978 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -125,7 +125,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() {
loadSubscriberCaptor.value.onSubscribe(Binder(), subscription)
canceller.run()
- verify(subscription).cancel()
+ verify(providers[0]).cancelSubscription(subscription)
}
@Test
@@ -145,7 +145,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() {
loadSubscriberCaptor.value.onComplete(b)
canceller.run()
- verify(subscription, never()).cancel()
+ verify(providers[0], never()).cancelSubscription(subscription)
}
@Test
@@ -203,7 +203,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() {
loadSubscriberCaptor.value.onError(b, "")
canceller.run()
- verify(subscription, never()).cancel()
+ verify(providers[0], never()).cancelSubscription(subscription)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 137a126f539d..300cb212ffb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -18,6 +18,7 @@ package com.android.systemui.globalactions;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
@@ -52,6 +53,8 @@ import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.RingerModeLiveData;
+import com.android.systemui.util.RingerModeTracker;
import org.junit.Before;
import org.junit.Test;
@@ -95,6 +98,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
@Mock private ControlsListingController mControlsListingController;
@Mock private ControlsController mControlsController;
@Mock private UiEventLogger mUiEventLogger;
+ @Mock private RingerModeTracker mRingerModeTracker;
+ @Mock private RingerModeLiveData mRingerModeLiveData;
private TestableLooper mTestableLooper;
@@ -103,6 +108,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
+
+ when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
mGlobalActionsDialog = new GlobalActionsDialog(mContext,
mWindowManagerFuncs,
mAudioManager,
@@ -133,7 +140,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
mBackgroundExecutor,
mControlsListingController,
mControlsController,
- mUiEventLogger
+ mUiEventLogger,
+ mRingerModeTracker
);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
index 260f52070a70..d407b8a1e449 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
@@ -67,10 +67,11 @@ public class SeekBarObserverTest : SysuiTestCase() {
val isEnabled = false
val data = SeekBarViewModel.Progress(isEnabled, false, null, null, null)
observer.onChanged(data)
- // THEN seek bar visibility is set to GONE
- assertThat(seekBarView.getVisibility()).isEqualTo(View.GONE)
- assertThat(elapsedTimeView.getVisibility()).isEqualTo(View.GONE)
- assertThat(totalTimeView.getVisibility()).isEqualTo(View.GONE)
+ // THEN seek bar shows just a line with no text
+ assertThat(seekBarView.isEnabled()).isFalse()
+ assertThat(seekBarView.getThumb().getAlpha()).isEqualTo(0)
+ assertThat(elapsedTimeView.getText()).isEqualTo("")
+ assertThat(totalTimeView.getText()).isEqualTo("")
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 43cf83f380f1..81f9546ba885 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -29,6 +29,7 @@ import android.service.notification.StatusBarNotification;
import com.android.internal.logging.InstanceId;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
import java.util.ArrayList;
@@ -44,16 +45,22 @@ import java.util.ArrayList;
public class NotificationEntryBuilder {
private final SbnBuilder mSbnBuilder = new SbnBuilder();
private final RankingBuilder mRankingBuilder = new RankingBuilder();
+ private final FakeSystemClock mClock = new FakeSystemClock();
private StatusBarNotification mSbn = null;
/* ListEntry properties */
private GroupEntry mParent;
private int mSection = -1;
+ /* If set, use this creation time instead of mClock.uptimeMillis */
+ private long mCreationTime = -1;
+
public NotificationEntry build() {
StatusBarNotification sbn = mSbn != null ? mSbn : mSbnBuilder.build();
mRankingBuilder.setKey(sbn.getKey());
- final NotificationEntry entry = new NotificationEntry(sbn, mRankingBuilder.build());
+ long creationTime = mCreationTime != -1 ? mCreationTime : mClock.uptimeMillis();
+ final NotificationEntry entry = new NotificationEntry(
+ sbn, mRankingBuilder.build(), mClock.uptimeMillis());
/* ListEntry properties */
entry.setParent(mParent);
@@ -86,6 +93,14 @@ public class NotificationEntryBuilder {
return this;
}
+ /**
+ * Set the creation time
+ */
+ public NotificationEntryBuilder setCreationTime(long creationTime) {
+ mCreationTime = creationTime;
+ return this;
+ }
+
/* Delegated to SbnBuilder */
public NotificationEntryBuilder setPkg(String pkg) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 5b0b66849027..1a022ec7c87d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -52,6 +52,7 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -71,6 +72,7 @@ public class NotificationEntryTest extends SysuiTestCase {
private int mId;
private NotificationEntry mEntry;
+ private final FakeSystemClock mClock = new FakeSystemClock();
@Before
public void setup() {
@@ -187,7 +189,7 @@ public class NotificationEntryTest extends SysuiTestCase {
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking);
+ new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index be026f7884c3..1c6e5a36bd8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.row;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
+
import static junit.framework.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
@@ -139,6 +141,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
private NotificationRowBinderImpl mRowBinder;
private Handler mHandler;
private FakeExecutor mBgExecutor;
+ private RowContentBindStage mRowContentBindStage;
@Before
public void setUp() {
@@ -147,10 +150,13 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
mHandler = Handler.createAsync(TestableLooper.get(this).getLooper());
+ // Add an action so heads up content views are made
+ Notification.Action action = new Notification.Action.Builder(null, null, null).build();
Notification notification = new Notification.Builder(mContext)
.setSmallIcon(R.drawable.ic_person)
.setContentTitle(TEST_TITLE)
.setContentText(TEST_TEXT)
+ .setActions(action)
.build();
mSbn = new SbnBuilder()
.setNotification(notification)
@@ -192,11 +198,11 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
() -> mock(SmartReplyController.class),
mock(ConversationNotificationProcessor.class),
mBgExecutor);
- RowContentBindStage stage = new RowContentBindStage(
+ mRowContentBindStage = new RowContentBindStage(
binder,
mock(NotifInflationErrorManager.class),
mock(RowContentBindStageLogger.class));
- pipeline.setStage(stage);
+ pipeline.setStage(mRowContentBindStage);
ArgumentCaptor<ExpandableNotificationRow> viewCaptor =
ArgumentCaptor.forClass(ExpandableNotificationRow.class);
@@ -232,7 +238,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
"FOOBAR", "FOOBAR",
mKeyguardBypassController,
mGroupManager,
- stage,
+ mRowContentBindStage,
mock(NotificationLogger.class),
mHeadsUpManager,
mPresenter,
@@ -256,7 +262,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
mRemoteInputManager,
mLockscreenUserManager,
pipeline,
- stage,
+ mRowContentBindStage,
mNotificationInterruptionStateProvider,
RowInflaterTask::new,
mExpandableNotificationRowComponentBuilder,
@@ -365,6 +371,27 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
verify(mPresenter).updateNotificationViews();
}
+ @Test
+ public void testContentViewInflationDuringRowInflationInflatesCorrectViews() {
+ // GIVEN a notification is added and the row is inflating
+ mEntryManager.addNotification(mSbn, mRankingMap);
+ ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
+ NotificationEntry.class);
+ verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture());
+ NotificationEntry entry = entryCaptor.getValue();
+
+ // WHEN we try to bind a content view
+ mRowContentBindStage.getStageParams(entry).requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
+ mRowContentBindStage.requestRebind(entry, null);
+
+ waitForInflation();
+
+ // THEN the notification has its row and all relevant content views inflated
+ assertNotNull(entry.getRow());
+ assertNotNull(entry.getRow().getPrivateLayout().getContractedChild());
+ assertNotNull(entry.getRow().getPrivateLayout().getHeadsUpChild());
+ }
+
/**
* Wait for inflation to finish.
*
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
new file mode 100644
index 000000000000..2489c301f6f5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.content.BroadcastReceiver
+import android.content.IntentFilter
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.lifecycle.Observer
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import org.junit.After
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class RingerModeLiveDataTest : SysuiTestCase() {
+
+ companion object {
+ private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ private fun <T> any(): T = Mockito.any()
+ private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+ private val INTENT = "INTENT"
+ }
+
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var valueSupplier: () -> Int
+ @Mock
+ private lateinit var observer: Observer<Int>
+ @Captor
+ private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
+ @Captor
+ private lateinit var intentFilterCaptor: ArgumentCaptor<IntentFilter>
+
+ // Run everything immediately
+ private val executor = Executor { it.run() }
+ private lateinit var liveData: RingerModeLiveData
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ liveData = RingerModeLiveData(broadcastDispatcher, executor, INTENT, valueSupplier)
+ }
+
+ @After
+ fun tearDown() {
+ liveData.removeObserver(observer)
+ }
+
+ @Test
+ fun testInit_broadcastNotRegistered() {
+ verifyNoMoreInteractions(broadcastDispatcher)
+ }
+
+ @Test
+ fun testOnActive_broadcastRegistered() {
+ liveData.observeForever(observer)
+ verify(broadcastDispatcher).registerReceiver(any(), any(), eq(executor), eq(UserHandle.ALL))
+ }
+
+ @Test
+ fun testOnActive_intentFilterHasIntent() {
+ liveData.observeForever(observer)
+ verify(broadcastDispatcher).registerReceiver(any(), capture(intentFilterCaptor), any(),
+ any())
+ assertTrue(intentFilterCaptor.value.hasAction(INTENT))
+ }
+
+ @Test
+ fun testOnActive_valueObtained() {
+ liveData.observeForever(observer)
+ verify(valueSupplier).invoke()
+ }
+
+ @Test
+ fun testOnInactive_broadcastUnregistered() {
+ liveData.observeForever(observer)
+ liveData.removeObserver(observer)
+ verify(broadcastDispatcher).unregisterReceiver(any())
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 8cc83dd99b53..6166cd76e005 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -31,6 +31,7 @@ import android.media.session.MediaSession;
import android.os.Handler;
import android.os.Process;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
@@ -38,7 +39,10 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.RingerModeLiveData;
+import com.android.systemui.util.RingerModeTracker;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,6 +53,7 @@ import java.util.Optional;
@RunWith(AndroidTestingRunner.class)
@SmallTest
+@TestableLooper.RunWithLooper
public class VolumeDialogControllerImplTest extends SysuiTestCase {
TestableVolumeDialogControllerImpl mVolumeController;
@@ -56,18 +61,35 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
StatusBar mStatusBar;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private RingerModeTracker mRingerModeTracker;
+ @Mock
+ private RingerModeLiveData mRingerModeLiveData;
+ @Mock
+ private RingerModeLiveData mRingerModeInternalLiveData;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
+ when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
+ when(mRingerModeTracker.getRingerModeInternal()).thenReturn(mRingerModeInternalLiveData);
+ // Initial non-set value
+ when(mRingerModeLiveData.getValue()).thenReturn(-1);
+ when(mRingerModeInternalLiveData.getValue()).thenReturn(-1);
+
mCallback = mock(VolumeDialogControllerImpl.C.class);
mStatusBar = mock(StatusBar.class);
mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mCallback, mStatusBar,
- mBroadcastDispatcher);
+ mBroadcastDispatcher, mRingerModeTracker);
mVolumeController.setEnableDialogs(true, true);
}
+ @After
+ public void tearDown() {
+ mVolumeController.destroy();
+ }
+
@Test
public void testRegisteredWithDispatcher() {
verify(mBroadcastDispatcher).registerReceiverWithHandler(any(BroadcastReceiver.class),
@@ -109,7 +131,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
TestableVolumeDialogControllerImpl
nullStatusBarTestableDialog =
new TestableVolumeDialogControllerImpl(
- mContext, callback, null, mBroadcastDispatcher);
+ mContext, callback, null, mBroadcastDispatcher, mRingerModeTracker);
nullStatusBarTestableDialog.setEnableDialogs(true, true);
nullStatusBarTestableDialog.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
verify(callback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
@@ -127,12 +149,26 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
mVolumeController.mMediaSessionsCallbacksW.onRemoteRemoved(token);
}
+ @Test
+ public void testRingerModeLiveDataObserving() {
+ verify(mRingerModeLiveData).observeForever(any());
+ verify(mRingerModeInternalLiveData).observeForever(any());
+ }
+
+ @Test
+ public void testRingerModeOnDestroy_observersRemoved() {
+ mVolumeController.destroy();
+
+ verify(mRingerModeLiveData).removeObserver(any());
+ verify(mRingerModeInternalLiveData).removeObserver(any());
+ }
+
static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl {
TestableVolumeDialogControllerImpl(Context context, C callback, StatusBar s,
- BroadcastDispatcher broadcastDispatcher) {
+ BroadcastDispatcher broadcastDispatcher, RingerModeTracker ringerModeTracker) {
super(
context, broadcastDispatcher,
- s == null ? Optional.empty() : Optional.of(() -> s));
+ s == null ? Optional.empty() : Optional.of(() -> s), ringerModeTracker);
mCallbacks = callback;
}
}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index b3a30abca6f1..6695eed0ff65 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -33,7 +33,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
@@ -72,8 +71,6 @@ import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -255,19 +252,16 @@ public final class EntitlementManagerTest {
@Test
public void testRequestLastEntitlementCacheValue() throws Exception {
- final CountDownLatch mCallbacklatch = new CountDownLatch(1);
// 1. Entitlement check is not required.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
ResultReceiver receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
@@ -277,12 +271,10 @@ public final class EntitlementManagerTest {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 3. No cache value and ui entitlement check is needed.
@@ -291,12 +283,10 @@ public final class EntitlementManagerTest {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(1, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement
@@ -306,12 +296,10 @@ public final class EntitlementManagerTest {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed.
@@ -320,12 +308,10 @@ public final class EntitlementManagerTest {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(1, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 6. Cache value is TETHER_ERROR_NO_ERROR.
@@ -334,12 +320,10 @@ public final class EntitlementManagerTest {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 7. Test get value for other downstream type.
@@ -347,12 +331,10 @@ public final class EntitlementManagerTest {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 8. Test get value for invalid downstream type.
@@ -361,22 +343,14 @@ public final class EntitlementManagerTest {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
}
- void callbackTimeoutHelper(final CountDownLatch latch) throws Exception {
- if (!latch.await(1, TimeUnit.SECONDS)) {
- fail("Timout, fail to receive callback");
- }
- }
-
@Test
public void verifyPermissionResult() {
setupForRequiredProvisioning();
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 93bffe9f54e4..52a82dd2a156 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -36,10 +36,12 @@ import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
import libcore.io.IoUtils;
@@ -261,22 +263,7 @@ public class WallpaperBackupAgent extends BackupAgent {
// And reset to the wallpaper service we should be using
ComponentName wpService = parseWallpaperComponent(infoStage, "wp");
- if (servicePackageExists(wpService)) {
- Slog.i(TAG, "Using wallpaper service " + wpService);
- mWm.setWallpaperComponent(wpService, UserHandle.USER_SYSTEM);
- if (!lockImageStage.exists()) {
- // We have a live wallpaper and no static lock image,
- // allow live wallpaper to show "through" on lock screen.
- mWm.clear(FLAG_LOCK);
- }
- } else {
- // If we've restored a live wallpaper, but the component doesn't exist,
- // we should log it as an error so we can easily identify the problem
- // in reports from users
- if (wpService != null) {
- Slog.e(TAG, "Wallpaper service " + wpService + " isn't available.");
- }
- }
+ updateWallpaperComponent(wpService, !lockImageStage.exists());
} catch (Exception e) {
Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage());
} finally {
@@ -293,6 +280,28 @@ public class WallpaperBackupAgent extends BackupAgent {
}
}
+ @VisibleForTesting
+ void updateWallpaperComponent(ComponentName wpService, boolean applyToLock) throws IOException {
+ if (servicePackageExists(wpService)) {
+ Slog.i(TAG, "Using wallpaper service " + wpService);
+ mWm.setWallpaperComponent(wpService, UserHandle.USER_SYSTEM);
+ if (applyToLock) {
+ // We have a live wallpaper and no static lock image,
+ // allow live wallpaper to show "through" on lock screen.
+ mWm.clear(FLAG_LOCK);
+ }
+ } else {
+ // If we've restored a live wallpaper, but the component doesn't exist,
+ // we should log it as an error so we can easily identify the problem
+ // in reports from users
+ if (wpService != null) {
+ applyComponentAtInstall(wpService, applyToLock);
+ Slog.w(TAG, "Wallpaper service " + wpService + " isn't available. "
+ + " Will try to apply later");
+ }
+ }
+ }
+
private void restoreFromStage(File stage, File info, String hintTag, int which)
throws IOException {
if (stage.exists()) {
@@ -372,7 +381,8 @@ public class WallpaperBackupAgent extends BackupAgent {
return (value == null) ? defValue : Integer.parseInt(value);
}
- private boolean servicePackageExists(ComponentName comp) {
+ @VisibleForTesting
+ boolean servicePackageExists(ComponentName comp) {
try {
if (comp != null) {
final IPackageManager pm = AppGlobals.getPackageManager();
@@ -401,4 +411,53 @@ public class WallpaperBackupAgent extends BackupAgent {
throws IOException {
// Intentionally blank
}
+
+ private void applyComponentAtInstall(ComponentName componentName, boolean applyToLock) {
+ PackageMonitor packageMonitor = getWallpaperPackageMonitor(componentName, applyToLock);
+ packageMonitor.register(getBaseContext(), null, UserHandle.ALL, true);
+ }
+
+ @VisibleForTesting
+ PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, boolean applyToLock) {
+ return new PackageMonitor() {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ if (!isDeviceInRestore()) {
+ // We don't want to reapply the wallpaper outside a restore.
+ unregister();
+ return;
+ }
+
+ if (componentName.getPackageName().equals(packageName)) {
+ Slog.d(TAG, "Applying component " + componentName);
+ mWm.setWallpaperComponent(componentName);
+ if (applyToLock) {
+ try {
+ mWm.clear(FLAG_LOCK);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to apply live wallpaper to lock screen: " + e);
+ }
+ }
+ // We're only expecting to restore the wallpaper component once.
+ unregister();
+ }
+ }
+ };
+ }
+
+ @VisibleForTesting
+ boolean isDeviceInRestore() {
+ try {
+ boolean isInSetup = Settings.Secure.getInt(getBaseContext().getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE) == 0;
+ boolean isInDeferredSetup = Settings.Secure.getInt(getBaseContext()
+ .getContentResolver(),
+ Settings.Secure.USER_SETUP_PERSONALIZATION_STATE) ==
+ Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED;
+ return isInSetup || isInDeferredSetup;
+ } catch (Settings.SettingNotFoundException e) {
+ Slog.w(TAG, "Failed to check if the user is in restore: " + e);
+ return false;
+ }
+ }
} \ No newline at end of file
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 255fdef36cd7..4367075abc74 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wallpaperbackup.tests;
+package com.android.wallpaperbackup;
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
@@ -26,17 +26,22 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.WallpaperManager;
import android.app.backup.FullBackupDataOutput;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.SharedPreferences;
import android.os.UserHandle;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.content.PackageMonitor;
import com.android.wallpaperbackup.WallpaperBackupAgent;
import com.android.wallpaperbackup.utils.ContextWithServiceOverrides;
@@ -58,6 +63,7 @@ import java.util.List;
public class WallpaperBackupAgentTest {
private static final String SYSTEM_GENERATION = "system_gen";
private static final String LOCK_GENERATION = "lock_gen";
+ private static final String TEST_WALLPAPER_PACKAGE = "wallpaper_package";
private static final int TEST_SYSTEM_WALLPAPER_ID = 1;
private static final int TEST_LOCK_WALLPAPER_ID = 2;
@@ -66,11 +72,13 @@ public class WallpaperBackupAgentTest {
@Mock private WallpaperManager mWallpaperManager;
@Mock private SharedPreferences mSharedPreferences;
@Mock private SharedPreferences.Editor mSharedPreferenceEditor;
+ @Mock private Context mMockContext;
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private ContextWithServiceOverrides mContext;
private IsolatedWallpaperBackupAgent mWallpaperBackupAgent;
+ private ComponentName mWallpaperComponent;
@Before
public void setUp() {
@@ -88,6 +96,8 @@ public class WallpaperBackupAgentTest {
mWallpaperBackupAgent = new IsolatedWallpaperBackupAgent(mTemporaryFolder.getRoot());
mWallpaperBackupAgent.attach(mContext);
mWallpaperBackupAgent.onCreate();
+
+ mWallpaperComponent = new ComponentName(TEST_WALLPAPER_PACKAGE, "");
}
@Test
@@ -130,6 +140,69 @@ public class WallpaperBackupAgentTest {
inOrder.verify(mSharedPreferenceEditor).apply();
}
+ @Test
+ public void updateWallpaperComponent_doesApplyLater() throws IOException {
+ mWallpaperBackupAgent.mIsDeviceInRestore = true;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ /* applyToLock */ true);
+
+ // Imitate wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+ /* uid */0);
+
+ verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent);
+ verify(mWallpaperManager, times(1)).clear(eq(FLAG_LOCK));
+ }
+
+ @Test
+ public void updateWallpaperComponent_applyToLockFalse_doesApplyLaterOnlyToMainScreen()
+ throws IOException {
+ mWallpaperBackupAgent.mIsDeviceInRestore = true;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ /* applyToLock */ false);
+
+ // Imitate wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+ /* uid */0);
+
+ verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent);
+ verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK));
+ }
+
+ @Test
+ public void updateWallpaperComponent_deviceNotInRestore_doesNotApply()
+ throws IOException {
+ mWallpaperBackupAgent.mIsDeviceInRestore = false;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ /* applyToLock */ true);
+
+ // Imitate wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+ /* uid */0);
+
+ verify(mWallpaperManager, never()).setWallpaperComponent(mWallpaperComponent);
+ verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK));
+ }
+
+ @Test
+ public void updateWallpaperComponent_differentPackageInstalled_doesNotApply()
+ throws IOException {
+ mWallpaperBackupAgent.mIsDeviceInRestore = false;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ /* applyToLock */ true);
+
+ // Imitate "wrong" wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(/* packageName */"",
+ /* uid */0);
+
+ verify(mWallpaperManager, never()).setWallpaperComponent(mWallpaperComponent);
+ verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK));
+ }
+
private void mockUnbackedUpState() {
mockCurrentWallpapers(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID);
when(mSharedPreferences.getInt(eq(SYSTEM_GENERATION), eq(-1))).thenReturn(-1);
@@ -162,6 +235,8 @@ public class WallpaperBackupAgentTest {
private class IsolatedWallpaperBackupAgent extends WallpaperBackupAgent {
File mWallpaperBaseDirectory;
List<File> mBackedUpFiles = new ArrayList<>();
+ PackageMonitor mWallpaperPackageMonitor;
+ boolean mIsDeviceInRestore = false;
IsolatedWallpaperBackupAgent(File wallpaperBaseDirectory) {
mWallpaperBaseDirectory = wallpaperBaseDirectory;
@@ -181,5 +256,27 @@ public class WallpaperBackupAgentTest {
public SharedPreferences getSharedPreferences(File file, int mode) {
return mSharedPreferences;
}
+
+ @Override
+ boolean servicePackageExists(ComponentName comp) {
+ return false;
+ }
+
+ @Override
+ boolean isDeviceInRestore() {
+ return mIsDeviceInRestore;
+ }
+
+ @Override
+ PackageMonitor getWallpaperPackageMonitor(ComponentName componentName,
+ boolean applyToLock) {
+ mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(componentName, applyToLock);
+ return mWallpaperPackageMonitor;
+ }
+
+ @Override
+ public Context getBaseContext() {
+ return mMockContext;
+ }
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
index ff59c24a7ca2..20a11bd9acd3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import android.annotation.NonNull;
+import android.app.ActivityManager;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -83,7 +84,7 @@ final class AccessibilityShellCommand extends ShellCommand {
return null;
}
}
- return UserHandle.USER_SYSTEM;
+ return ActivityManager.getCurrentUser();
}
@Override
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 8c1360cca940..ca1b27bd261e 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -3669,6 +3669,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// Default launcher from package manager.
final ComponentName defaultLauncher = mPackageManagerInternal
.getDefaultHomeActivity(UserHandle.getUserId(callingUid));
+ if (defaultLauncher == null) {
+ return;
+ }
int defaultLauncherUid = 0;
try {
defaultLauncherUid = mPackageManager.getApplicationInfo(
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 8b50b010a22c..b6bc7c5646a5 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -58,6 +58,7 @@ import com.android.internal.os.IResultReceiver;
import com.android.server.autofill.ui.InlineSuggestionFactory;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
@@ -263,6 +264,8 @@ final class RemoteAugmentedAutofillService
&& fieldIds.get(0).equals(focusedId);
client.autofill(sessionId, fieldIds, dataset.getFieldValues(),
hideHighlight);
+ inlineSuggestionsCallback.apply(new InlineSuggestionsResponse(
+ Collections.EMPTY_LIST));
} catch (RemoteException e) {
Slog.w(TAG, "Encounter exception autofilling the values");
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
index 7ad5632dd70e..347174c4c804 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
@@ -27,7 +27,9 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteCallback;
import android.service.autofill.IInlineSuggestionRenderService;
import android.service.autofill.IInlineSuggestionUiCallback;
import android.service.autofill.InlinePresentation;
@@ -91,6 +93,15 @@ public final class RemoteInlineSuggestionRenderService extends
hostInputToken, displayId));
}
+ /**
+ * Gets the inline suggestions renderer info as a {@link Bundle}.
+ */
+ public void getInlineSuggestionsRendererInfo(@NonNull RemoteCallback callback) {
+ scheduleAsyncRequest((s) -> s.getInlineSuggestionsRendererInfo(new RemoteCallback(
+ (bundle) -> callback.sendResult(bundle)
+ )));
+ }
+
@Nullable
private static ServiceInfo getServiceInfo(Context context, int userId) {
final String packageName =
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 4ecffd8d29b0..3d6861898aaf 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -652,10 +652,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return mService.isInlineSuggestionsEnabled();
}
- private boolean isInlineSuggestionRenderServiceAvailable() {
- return mService.getRemoteInlineSuggestionRenderServiceLocked() != null;
- }
-
/**
* Clears the existing response for the partition, reads a new structure, and then requests a
* new fill response from the fill service.
@@ -715,14 +711,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Only ask IME to create inline suggestions request if Autofill provider supports it and
// the render service is available.
- if (isInlineSuggestionsEnabledByAutofillProviderLocked()
- && isInlineSuggestionRenderServiceAvailable()) {
+ final RemoteInlineSuggestionRenderService remoteRenderService =
+ mService.getRemoteInlineSuggestionRenderServiceLocked();
+ if (isInlineSuggestionsEnabledByAutofillProviderLocked() && remoteRenderService != null) {
Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ true);
if (inlineSuggestionsRequestConsumer != null) {
- // TODO(b/146454892): pipe the uiExtras from the ExtServices.
- mInlineSessionController.onCreateInlineSuggestionsRequestLocked(mCurrentViewId,
- inlineSuggestionsRequestConsumer, Bundle.EMPTY);
+ remoteRenderService.getInlineSuggestionsRendererInfo(
+ new RemoteCallback((extras) -> {
+ mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
+ mCurrentViewId, inlineSuggestionsRequestConsumer, extras);
+ }
+ ));
}
} else {
mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ false);
@@ -1228,6 +1228,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting to hide fill UI", e);
}
+
+ mInlineSessionController.hideInlineSuggestionsUiLocked(id);
}
}
@@ -3128,13 +3130,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// 1. the field is augmented autofill only (when standard autofill provider is None or
// when it returns null response)
// 2. standard autofill provider doesn't support inline suggestion
- if (isInlineSuggestionRenderServiceAvailable()
+ final RemoteInlineSuggestionRenderService remoteRenderService =
+ mService.getRemoteInlineSuggestionRenderServiceLocked();
+ if (remoteRenderService != null
&& (mForAugmentedAutofillOnly
|| !isInlineSuggestionsEnabledByAutofillProviderLocked())) {
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
- // TODO(b/146454892): pipe the uiExtras from the ExtServices.
- mInlineSessionController.onCreateInlineSuggestionsRequestLocked(mCurrentViewId,
- /*requestConsumer=*/ requestAugmentedAutofill, Bundle.EMPTY);
+ remoteRenderService.getInlineSuggestionsRendererInfo(new RemoteCallback(
+ (extras) -> {
+ mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
+ mCurrentViewId, /*requestConsumer=*/ requestAugmentedAutofill,
+ extras);
+ }, mHandler));
} else {
requestAugmentedAutofill.accept(
mInlineSessionController.getInlineSuggestionsRequestLocked().orElse(null));
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 192ea72224b1..3c0d880916ee 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -119,8 +119,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private static final int MESSAGE_DISABLE = 2;
private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3;
private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4;
- private static final int MESSAGE_REGISTER_ADAPTER = 20;
- private static final int MESSAGE_UNREGISTER_ADAPTER = 21;
private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
@@ -642,10 +640,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
Slog.w(TAG, "Callback is null in registerAdapter");
return null;
}
- Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
- msg.obj = callback;
- mHandler.sendMessage(msg);
-
+ synchronized (mCallbacks) {
+ mCallbacks.register(callback);
+ }
return mBluetooth;
}
@@ -655,9 +652,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return;
}
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
- msg.obj = callback;
- mHandler.sendMessage(msg);
+ synchronized (mCallbacks) {
+ mCallbacks.unregister(callback);
+ }
}
public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
@@ -1559,18 +1556,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
* Inform BluetoothAdapter instances that Adapter service is up
*/
private void sendBluetoothServiceUpCallback() {
- try {
- int n = mCallbacks.beginBroadcast();
- Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
- for (int i = 0; i < n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+ synchronized (mCallbacks) {
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+ for (int i = 0; i < n; i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+ }
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
- } finally {
- mCallbacks.finishBroadcast();
}
}
@@ -1578,18 +1577,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
* Inform BluetoothAdapter instances that Adapter service is down
*/
private void sendBluetoothServiceDownCallback() {
- try {
- int n = mCallbacks.beginBroadcast();
- Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
- for (int i = 0; i < n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+ synchronized (mCallbacks) {
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+ for (int i = 0; i < n; i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+ }
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
- } finally {
- mCallbacks.finishBroadcast();
}
}
@@ -1917,17 +1918,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mContext.getPackageName());
}
break;
-
- case MESSAGE_REGISTER_ADAPTER: {
- IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
- mCallbacks.register(callback);
- break;
- }
- case MESSAGE_UNREGISTER_ADAPTER: {
- IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
- mCallbacks.unregister(callback);
- break;
- }
case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: {
IBluetoothStateChangeCallback callback =
(IBluetoothStateChangeCallback) msg.obj;
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index a9c3079adcc0..7f25de6b3470 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1836,12 +1836,13 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
- PendingIntent intent, String packageName, String featureId) {
+ PendingIntent intent, String packageName, String featureId, String listenerId) {
if (request == null) {
request = DEFAULT_LOCATION_REQUEST;
}
- CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
+ CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId,
+ listenerId);
identity.enforceLocationPermission();
WorkSource workSource = request.getWorkSource();
@@ -2027,7 +2028,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean getCurrentLocation(LocationRequest locationRequest,
ICancellationSignal remoteCancellationSignal, ILocationListener listener,
- String packageName, String featureId) {
+ String packageName, String featureId, String listenerId) {
// side effect of validating locationRequest and packageName
Location lastLocation = getLastLocation(locationRequest, packageName, featureId);
if (lastLocation != null) {
@@ -2052,7 +2053,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- requestLocationUpdates(locationRequest, listener, null, packageName, featureId);
+ requestLocationUpdates(locationRequest, listener, null, packageName, featureId, listenerId);
CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
remoteCancellationSignal);
if (cancellationSignal != null) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 9aefc8dc78ec..b0a586d122ea 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -220,6 +220,10 @@ class StorageManagerService extends IStorageManager.Stub
private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage();
+ // A system property to control if obb app data isolation is enabled in vold.
+ private static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
+ "persist.sys.vold_app_data_isolation_enabled";
+
/**
* If {@code 1}, enables the isolated storage feature. If {@code -1},
* disables the isolated storage feature. If {@code 0}, uses the default
@@ -596,6 +600,8 @@ class StorageManagerService extends IStorageManager.Stub
private final boolean mIsFuseEnabled;
+ private final boolean mVoldAppDataIsolationEnabled;
+
@GuardedBy("mLock")
private final Set<Integer> mUidsWithLegacyExternalStorage = new ArraySet<>();
// Not guarded by lock, always used on the ActivityManager thread
@@ -1516,7 +1522,7 @@ class StorageManagerService extends IStorageManager.Stub
if (vol.type == VolumeInfo.TYPE_EMULATED) {
if (newState != VolumeInfo.STATE_MOUNTED) {
mFuseMountedUser.remove(vol.getMountUserId());
- } else {
+ } else if (mVoldAppDataIsolationEnabled){
final int userId = vol.getMountUserId();
mFuseMountedUser.add(userId);
// Async remount app storage so it won't block the main thread.
@@ -1740,6 +1746,8 @@ class StorageManagerService extends IStorageManager.Stub
// incorrect until #updateFusePropFromSettings where we set the correct value and reboot if
// different
mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED);
+ mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean(
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
mContext = context;
mResolver = mContext.getContentResolver();
mCallbacks = new Callbacks(FgThread.get().getLooper());
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index bc455338fc52..8f8990fdaae7 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -98,7 +98,7 @@ class AnrHelper {
final long endTime = SystemClock.uptimeMillis();
Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in "
+ (endTime - startTime) + "ms, latency " + reportLatency
- + (onlyDumpSelf ? "ms" : "ms (expired, only dump ANR app)"));
+ + (onlyDumpSelf ? "ms (expired, only dump ANR app)" : "ms"));
}
mRunning.set(false);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index dbcb3da3e2f4..2d6ef81faf1c 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -231,7 +231,8 @@ public final class OomAdjuster {
final ServiceThread adjusterThread =
new ServiceThread(TAG, TOP_APP_PRIORITY_BOOST, false /* allowIo */);
adjusterThread.start();
- Process.setThreadGroupAndCpuset(adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP);
+ adjusterThread.getThreadHandler().post(() -> Process.setThreadGroupAndCpuset(
+ adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP));
return adjusterThread;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.md b/services/core/java/com/android/server/am/OomAdjuster.md
new file mode 100644
index 000000000000..eda511ab7369
--- /dev/null
+++ b/services/core/java/com/android/server/am/OomAdjuster.md
@@ -0,0 +1,129 @@
+<!-- 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.
+-->
+
+# Oom Adjuster Designs
+
+## Purpose of Oom Adjuster
+
+The Android OS runs with limited hardware resources, i.e. CPU/RAM/Power. To strive for the better performance, Oom Ajuster is introduced to tweak the following 3 major factors:
+
+ * Process State
+ * Wildly used by the System Server, i.e., determine if it's foreground or not, change the GC behavior, etc.
+ * Defined in `ActivityManager#PROCESS_STATE_*`
+ * Oom Adj score
+ * Used by the lmkd to determine which process should be expunged on memory pressure.
+ * Defined in `ProcessList#*_ADJ`
+ * Scheduler Group
+ * Used to tweak the process group, thread priorities.
+ * Top process is scheduled to be running on a dedicated big core, while foreground processes take the other big cores; background processes stay with LITTLE cores instead.
+
+## Process Capabilities
+
+Besides the above 3 major factors, Android R introduced the Process Capabilities `ActivityManager#PROCESS_CAPABILITY_*`. It's a new attribute to process record, mainly designed for supporting the "while-in-use" permission model - in additional to the traditional Android permissions, wheather or not a process has access to a given API, will be guarded by its current process state as well. The OomAdjuster will compute the process capabilities during updating the oom adj. Meanwhile, the flag `ActivityManager#BIND_INCLUDE_CAPABILITIES` enables to possiblity to "transfer" the capability from a client process to the service process it binds to.
+
+## Rationale of Oom Adjuster
+
+System server keeps a list of recent used app processes. Given the 4 types of entities that an Android processes could have: Activity, Service, Content Provider and Broadcast Receiver, the System Server has to adjust the above 3 factors to give the users the best performance according to the states of the entities. A typical case would be that: foreground app A binds into a background service B in order to serve the user, in the case of memory pressure, the background service B should be avoided from being expunged since it would result user-perceptible interruption of service. The Oom Adjuster is to tweak the aforementioned 3 factors for those app processes.
+
+The timing of updating the Oom Adj score is vital: assume a camera process in background gets launched into foreground, launching camera typically incurs high memory pressure, which could incur low memory kills - if the camera process isn't moved out of the background adj group, it could get killed by lmkd. Therefore the updates have to be called pretty frequently: in case there is an activity start, service binding, etc.
+
+The update procedure basically consists of 3 parts:
+ * Find out the process record to be updated
+ * There are two categories of updateOomAdjLocked: one with the target process record to be updated, while the other one is to update all process record.
+ * Besides that, while computing the Oom Aj score, the clients of service connections or content providers of the present process record, which forms a process dependency graph actually, will be evaluated as well.
+ * Starting from Android R, when updating for a specific process record, an optimization is made that, only the reachable process records starting from this process record in the process dependency graph, will be re-evaluated.
+ * The `cached` Oom Adj scores are grouped in `bucket`, which is used in the isolated processes: they could be correlated - assume one isolated Chrome process is at Oom Adj score 920 and another one is 980; the later one could get expunged much earlier than the former one, which doesn't make sense; grouping them would be a big relief for this case.
+ * Compute Oom Adj score
+ * This procedure returns true if there is a score change, false if there is no.
+ * The curAdj field in the process record is used as an intermediate value during the computation.
+ * Initialize the Process State to `PROCESS_STATE_CACHED_EMPTY`, which is the lowest importance.
+ * Calculate the scores based on various factors:
+ * If it's not allowed to be lower than `ProcessList#FOREGROUND_APP_ADJ`, meaning it's propbably a persistent process, there is no too much to do here.
+ * Exame if the process is the top app, running remote animation, running instrumentation, receiving broadcast, executing services, running on top but sleeping (screen off), update the intermediate values.
+ * Ask Window Manager (yes, ActivityTaskManager is with WindowManager now) to tell each activity's visibility information.
+ * Check if the process has recent tasks, check if it's hosting a foreground service, overlay UI, toast etc. Note for the foreground service, if it was in foreground status, allow it to stay in higher rank in memory for a while: Assuming a camera captureing case, where the camera app is still processing the picture while being switched out of foreground - keep it stay in higher rank in memory would ensure the pictures are persisted correctly.
+ * Check if the process is the heavy weight process, whose launching/exiting would be slow and it's better to keep it in the memory. Note there should be only one heavy weight process across the system.
+ * For sure the Home process shouldn't be expunged frequently as well.
+ * The next two factors are either it was the previous process with visible UI to the user, or it's a backup agent.
+ * And then it goes to the massive searches against the service connections and the content providers, each of the clients will be evaluated, and the Oom Adj score could get updated according to its clients' scores. However there are a bunch of service binding flags which could impact the result:
+ * Below table captures the results with given various service binding states:
+ | Conditon #1 | Condition #2 | Condition #3 | Condition #4 | Result |
+ |---------------------------------|------------------------------------------------------------|----------------------------------------------|---------------------------------------------------|--------------------------|
+ | `BIND_WAIVE_PRIORITY` not set | `BIND_ALLOW_OOM_MANAGEMENT` set | Shown UI && Not Home | | Use the app's own Adj |
+ | | | Inactive for a while | | Use the app's own Adj |
+ | | Client has a higher importance | Shown UI && Not Home && client is invisible | | Use the app's own Adj |
+ | | | `BIND_ABOVE_CLIENT` and `BIND_IMPORTANT` set | Client is not persistent | Try client's Adj |
+ | | | | Client is persistent | Try persistent Adj |
+ | | | `BIND_NOT_PERCEPTIBLE` set | client < perceptible && app > low perceptible | Try low perceptible Adj |
+ | | | `BIND_NOT_VISIBLE` set | client < perceptible && app > perceptible | Try perceptible Adj |
+ | | | Client >= perceptible | | Try client's Adj |
+ | | | Adj > visible | | Max of client/Own Adj |
+ | | | | | Use the app's own Adj |
+ | | `BIND_NOT_FOREGROUND`+`BIND_IMPORTANT_BACKGROUND` not set | Client's sched group > app's | `BIND_IMPORTANT` is set | Use client's sched group |
+ | | | | | Use default sched group |
+ | | | Client's process state < top | `BIND_FOREGROUND_SERVICE` is set | ProcState = bound fg |
+ | | | | `BIND_FOREGROUND_SERVICE_WHILE_AWAKE` + screen ON | ProcState = bound fg |
+ | | | | | ProcState = important fg |
+ | | | Client's process state = top | | ProcState = bound top |
+ | | `BIND_IMPORTANT_BACKGROUND` not set | Client's process state < transient bg | | ProcState = transient bg |
+ | | `BIND_NOT_FOREGROUND` or `BIND_IMPORTANT_BACKGROUND` set | Client's process state < important bg | | ProcState = important bg |
+ | `BIND_ADJUST_WITH_ACTIVITY` set | Adj > fg && App visible | | | Adj = foreground |
+ | | | `BIND_NOT_FOREGROUND` not set | `BIND_IMPORTANT` is set | Sched = top app bound |
+ | | | | `BIND_IMPORTANT` is NOT set | Sched = default |
+ * Below table captures the results with given various content provider binding states:
+ | Conditon #1 | Condition #2 | Condition #3 | Result |
+ |---------------------------------|------------------------------------------------------------|----------------------------------------------|--------------------------|
+ | Client's process state >= cached| | | Client ProcState = empty |
+ | Adj > Client Adj | Not shown UI or is Home, or Client's Adj <= perceptible | Client's Adj <= foreground Adj | Try foreground Adj |
+ | | | Client's Adj > foreground Adj | Try client's Adj |
+ | Client's process state <= fg svc| Client's process state is top | | ProcState = bound top |
+ | | Client's process state is NOT top | | ProcState = bound fg svc |
+ | Has external dependencies | Adj > fg app | | adj = fg app |
+ | | Process state > important foreground | | ProcState = important fg |
+ | Still within retain time | Adj > previous app Adj | | adj = previuos app adj |
+ | | Process state > last activity | | ProcState = last activity|
+ * Some additional tweaks after the above ones:
+ | Conditon #1 | Condition #2 | Condition #3 | Result |
+ |---------------------------------|------------------------------------------------------------|----------------------------------------------|------------------------------------|
+ | Process state >= cached empty | Has client activities | | ProcState = cached activity client |
+ | | treat like activity (IME) | | ProcState = cached activity |
+ | Adj is service adj | computing all process records | Num of new service A > 1/3 of services | Push it to service B |
+ | | | Low on RAM and app process's PSS is large | Push it to service B |
+ * Apply the scores, which consists of: write into kernel sysfs entries to update the Oom Adj scores; call kernel API to set the thread priorities, and then tell the world the new process state
+
+## Cycles, Cycles, Cycles
+
+Another interesting aspect of the Oom Adjuster is the cycles of the dependencies. A simple example would be like below illustration, process A is hosting a service which is bound by process B; meanwhile the process B is hosting a service which is bound by process A.
+<pre>
+ +-------------+ +-------------+
+ | Process A | <-------- | Process B |
+ | (service 1) | --------> | (service 2) |
+ +-------------+ +-------------+
+</pre>
+
+There could be very complicated cases, which could involve multiple cycles, and in the dependency graph, each of the process record node could have different importance.
+<pre>
+ +-------------+ +-------------+ +-------------+ +-------------+ +-------------+
+ | Process D | --------> | Process A | <-------- | Process B | <-------- | Process C | <-------- | Process A |
+ | | | (service 1) | | (service 2) | | (service 3) | | (service 1) |
+ +-------------+ +-------------+ +-------------+ +-------------+ +-------------+
+</pre>
+
+The Oom Adjuster maintains a global sequence ID `mAdjSeq` to track the current Oom Adjuster calling. And each of the process record has a field to track in which sequence the process record is evaluated. If during the Oom Adj computation, a process record with sequence ID as same as the current global sequence ID, this would mean that a cycle is detected; in this case:
+ * Decrement the sequence ID of each process if there is a cycle.
+ * Re-evaluate each of the process record within the cycle until nothing was promoted.
+ * Iterate the processes from least important to most important ones.
+ * A maximum retries of 10 is enforced, while in practice, the maximum retries could reach only 2 to 3.
+
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 786e9cf66bfa..89fa02bbbd64 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -154,6 +154,9 @@ public final class ProcessList {
static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.sys.vold_app_data_isolation_enabled";
+ // A system property to control if fuse is enabled.
+ static final String ANDROID_FUSE_ENABLED = "persist.sys.fuse";
+
// The minimum time we allow between crashes, for us to consider this
// application to be bad and stop and its services and reject broadcasts.
static final int MIN_CRASH_INTERVAL = 60 * 1000;
@@ -707,8 +710,13 @@ public final class ProcessList {
// want some apps enabled while some apps disabled
mAppDataIsolationEnabled =
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
- mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
+ boolean fuseEnabled = SystemProperties.getBoolean(ANDROID_FUSE_ENABLED, false);
+ boolean voldAppDataIsolationEnabled = SystemProperties.getBoolean(
ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+ if (!fuseEnabled && voldAppDataIsolationEnabled) {
+ Slog.e(TAG, "Fuse is not enabled while vold app data isolation is enabled");
+ }
+ mVoldAppDataIsolationEnabled = fuseEnabled && voldAppDataIsolationEnabled;
mAppDataIsolationWhitelistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 30e765f3d602..02d8571c2dcd 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3622,7 +3622,15 @@ public class AudioService extends IAudioService.Stub
hdlr = h;
// Remove from client list so that it is re-inserted at top of list
iter.remove();
- hdlr.getBinder().unlinkToDeath(hdlr, 0);
+ try {
+ hdlr.getBinder().unlinkToDeath(hdlr, 0);
+ if (cb != hdlr.getBinder()) {
+ hdlr = null;
+ }
+ } catch (NoSuchElementException e) {
+ hdlr = null;
+ Log.w(TAG, "link does not exist ...");
+ }
break;
}
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 4431abe43136..808f8c21cc8d 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -558,8 +558,10 @@ public abstract class BiometricServiceBase extends SystemService
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
statsModality(), BiometricsProtoEnums.ISSUE_CANCEL_TIMED_OUT);
+ ClientMonitor newClient = mPendingClient;
mCurrentClient = null;
- startClient(mPendingClient, false);
+ mPendingClient = null;
+ startClient(newClient, false);
}
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 962f337a8b3f..9a910bf5e859 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -569,9 +569,10 @@ public final class ContentService extends IContentService.Stub {
// Immediately dispatch notifications to foreground apps that
// are important to the user; all other background observers are
// delayed to avoid stampeding
+ final boolean noDelay = (key.flags & ContentResolver.NOTIFY_NO_DELAY) != 0;
final int procState = LocalServices.getService(ActivityManagerInternal.class)
.getUidProcessState(key.uid);
- if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND || noDelay) {
task.run();
} else {
BackgroundThread.getHandler().postDelayed(task, BACKGROUND_OBSERVER_DELAY);
diff --git a/services/core/java/com/android/server/display/color/AppSaturationController.java b/services/core/java/com/android/server/display/color/AppSaturationController.java
index e42be02d4a9e..6a685bf187ad 100644
--- a/services/core/java/com/android/server/display/color/AppSaturationController.java
+++ b/services/core/java/com/android/server/display/color/AppSaturationController.java
@@ -17,6 +17,7 @@
package com.android.server.display.color;
import android.annotation.UserIdInt;
+import android.util.ArrayMap;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -61,11 +62,12 @@ class AppSaturationController {
* Set the saturation level ({@code ColorDisplayManager#SaturationLevel} constant for a given
* package name and userId.
*/
- public boolean setSaturationLevel(String packageName, @UserIdInt int userId,
+ public boolean setSaturationLevel(String callingPackageName, String affectedPackageName,
+ @UserIdInt int userId,
int saturationLevel) {
synchronized (mLock) {
- return getSaturationControllerLocked(packageName, userId)
- .setSaturationLevel(saturationLevel);
+ return getSaturationControllerLocked(affectedPackageName, userId)
+ .setSaturationLevel(callingPackageName, saturationLevel);
}
}
@@ -148,13 +150,19 @@ class AppSaturationController {
private static class SaturationController {
+ private static final int FULL_SATURATION = 100;
+
private final List<WeakReference<ColorTransformController>> mControllerRefs =
new ArrayList<>();
- private int mSaturationLevel = 100;
+ private final ArrayMap<String, Integer> mSaturationLevels = new ArrayMap<>();
private float[] mTransformMatrix = new float[9];
- private boolean setSaturationLevel(int saturationLevel) {
- mSaturationLevel = saturationLevel;
+ private boolean setSaturationLevel(String callingPackageName, int saturationLevel) {
+ if (saturationLevel == FULL_SATURATION) {
+ mSaturationLevels.remove(callingPackageName);
+ } else {
+ mSaturationLevels.put(callingPackageName, saturationLevel);
+ }
if (!mControllerRefs.isEmpty()) {
return updateState();
}
@@ -163,17 +171,27 @@ class AppSaturationController {
private boolean addColorTransformController(
WeakReference<ColorTransformController> controller) {
+ clearExpiredReferences();
mControllerRefs.add(controller);
- if (mSaturationLevel != 100) {
+ if (!mSaturationLevels.isEmpty()) {
return updateState();
- } else {
- clearExpiredReferences();
}
return false;
}
+ private int calculateSaturationLevel() {
+ int saturationLevel = FULL_SATURATION;
+ for (int i = 0; i < mSaturationLevels.size(); i++) {
+ final int level = mSaturationLevels.valueAt(i);
+ if (level < saturationLevel) {
+ saturationLevel = level;
+ }
+ }
+ return saturationLevel;
+ }
+
private boolean updateState() {
- computeGrayscaleTransformMatrix(mSaturationLevel / 100f, mTransformMatrix);
+ computeGrayscaleTransformMatrix(calculateSaturationLevel() / 100f, mTransformMatrix);
boolean updated = false;
final Iterator<WeakReference<ColorTransformController>> iterator = mControllerRefs
@@ -190,7 +208,6 @@ class AppSaturationController {
}
}
return updated;
-
}
private void clearExpiredReferences() {
@@ -206,7 +223,7 @@ class AppSaturationController {
}
private void dump(PrintWriter pw) {
- pw.println(" mSaturationLevel: " + mSaturationLevel);
+ pw.println(" mSaturationLevels: " + mSaturationLevels);
pw.println(" mControllerRefs count: " + mControllerRefs.size());
}
}
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 2dc2cf0d8e90..95a98f1e9494 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -44,6 +44,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.display.ColorDisplayManager;
@@ -73,6 +74,7 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.DisplayThread;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
@@ -817,9 +819,11 @@ public final class ColorDisplayService extends SystemService {
return LocalDateTime.MIN;
}
- private boolean setAppSaturationLevelInternal(String packageName, int saturationLevel) {
+ private boolean setAppSaturationLevelInternal(String callingPackageName,
+ String affectedPackageName, int saturationLevel) {
return mAppSaturationController
- .setSaturationLevel(packageName, mCurrentUser, saturationLevel);
+ .setSaturationLevel(callingPackageName, affectedPackageName, mCurrentUser,
+ saturationLevel);
}
private void setColorModeInternal(@ColorMode int colorMode) {
@@ -1533,9 +1537,11 @@ public final class ColorDisplayService extends SystemService {
getContext().enforceCallingPermission(
Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
"Permission required to set display saturation level");
+ final String callingPackageName = LocalServices.getService(PackageManagerInternal.class)
+ .getNameForUid(Binder.getCallingUid());
final long token = Binder.clearCallingIdentity();
try {
- return setAppSaturationLevelInternal(packageName, level);
+ return setAppSaturationLevelInternal(callingPackageName, packageName, level);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/location/AppOpsHelper.java b/services/core/java/com/android/server/location/AppOpsHelper.java
index 9c279166ac8a..cb64c50bf11d 100644
--- a/services/core/java/com/android/server/location/AppOpsHelper.java
+++ b/services/core/java/com/android/server/location/AppOpsHelper.java
@@ -191,7 +191,7 @@ public class AppOpsHelper {
callerIdentity.uid,
callerIdentity.packageName,
callerIdentity.featureId,
- null) == AppOpsManager.MODE_ALLOWED;
+ callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -210,7 +210,7 @@ public class AppOpsHelper {
callerIdentity.packageName,
false,
callerIdentity.featureId,
- null) == AppOpsManager.MODE_ALLOWED;
+ callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -245,7 +245,7 @@ public class AppOpsHelper {
callerIdentity.uid,
callerIdentity.packageName,
callerIdentity.featureId,
- null) == AppOpsManager.MODE_ALLOWED;
+ callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/location/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java
index b84fd13415fc..8d508bb6256d 100644
--- a/services/core/java/com/android/server/location/CallerIdentity.java
+++ b/services/core/java/com/android/server/location/CallerIdentity.java
@@ -83,12 +83,22 @@ public final class CallerIdentity {
*/
public static CallerIdentity fromBinder(Context context, String packageName,
@Nullable String featureId) {
+ return fromBinder(context, packageName, featureId, null);
+ }
+
+ /**
+ * Creates a CallerIdentity from the current binder identity, using the given package, feature
+ * id, and listener id. The package will be checked to enforce it belongs to the calling uid,
+ * and a security exception will be thrown if it is invalid.
+ */
+ public static CallerIdentity fromBinder(Context context, String packageName,
+ @Nullable String featureId, @Nullable String listenerId) {
int uid = Binder.getCallingUid();
if (!ArrayUtils.contains(context.getPackageManager().getPackagesForUid(uid), packageName)) {
throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid);
}
- return fromBinderUnsafe(context, packageName, featureId);
+ return fromBinderUnsafe(context, packageName, featureId, listenerId);
}
/**
@@ -99,8 +109,19 @@ public final class CallerIdentity {
*/
public static CallerIdentity fromBinderUnsafe(Context context, String packageName,
@Nullable String featureId) {
+ return fromBinderUnsafe(context, packageName, featureId, null);
+ }
+
+ /**
+ * Creates a CallerIdentity from the current binder identity, using the given package, feature
+ * id, and listener id. The package will not be checked to enforce that it belongs to the
+ * calling uid - this method should only be used if the package will be validated by some other
+ * means, such as an appops call.
+ */
+ public static CallerIdentity fromBinderUnsafe(Context context, String packageName,
+ @Nullable String featureId, @Nullable String listenerId) {
return new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(),
- UserHandle.getCallingUserId(), packageName, featureId,
+ UserHandle.getCallingUserId(), packageName, featureId, listenerId,
getBinderPermissionLevel(context));
}
@@ -157,6 +178,9 @@ public final class CallerIdentity {
/** The calling feature id. */
public final @Nullable String featureId;
+ /** The calling listener id. */
+ public final @Nullable String listenerId;
+
/**
* The calling location permission level. This field should only be used for validating
* permissions for API access. It should not be used for validating permissions for location
@@ -167,11 +191,18 @@ public final class CallerIdentity {
@VisibleForTesting
public CallerIdentity(int uid, int pid, int userId, String packageName,
@Nullable String featureId, @PermissionLevel int permissionLevel) {
+ this(uid, pid, userId, packageName, featureId, null, permissionLevel);
+ }
+
+ private CallerIdentity(int uid, int pid, int userId, String packageName,
+ @Nullable String featureId, @Nullable String listenerId,
+ @PermissionLevel int permissionLevel) {
this.uid = uid;
this.pid = pid;
this.userId = userId;
this.packageName = Objects.requireNonNull(packageName);
this.featureId = featureId;
+ this.listenerId = listenerId;
this.permissionLevel = Preconditions.checkArgumentInRange(permissionLevel, PERMISSION_NONE,
PERMISSION_FINE, "permissionLevel");
}
@@ -216,7 +247,8 @@ public final class CallerIdentity {
return uid == that.uid
&& pid == that.pid
&& packageName.equals(that.packageName)
- && Objects.equals(featureId, that.featureId);
+ && Objects.equals(featureId, that.featureId)
+ && Objects.equals(listenerId, that.listenerId);
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 193107996d1e..c3413e8d2934 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -98,18 +98,26 @@ class MediaRouter2ServiceImpl {
public List<MediaRoute2Info> getSystemRoutes() {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+ final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ == PackageManager.PERMISSION_GRANTED;
final long token = Binder.clearCallingIdentity();
try {
Collection<MediaRoute2Info> systemRoutes;
synchronized (mLock) {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
- MediaRoute2ProviderInfo providerInfo =
- userRecord.mHandler.mSystemProvider.getProviderInfo();
- if (providerInfo != null) {
- systemRoutes = providerInfo.getRoutes();
+ if (hasModifyAudioRoutingPermission) {
+ MediaRoute2ProviderInfo providerInfo =
+ userRecord.mHandler.mSystemProvider.getProviderInfo();
+ if (providerInfo != null) {
+ systemRoutes = providerInfo.getRoutes();
+ } else {
+ systemRoutes = Collections.emptyList();
+ }
} else {
- systemRoutes = Collections.emptyList();
+ systemRoutes = new ArrayList<>();
+ systemRoutes.add(userRecord.mHandler.mSystemProvider.getDefaultRoute());
}
}
return new ArrayList<>(systemRoutes);
@@ -122,18 +130,25 @@ class MediaRouter2ServiceImpl {
public RoutingSessionInfo getSystemSessionInfo() {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+ final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ == PackageManager.PERMISSION_GRANTED;
final long token = Binder.clearCallingIdentity();
try {
RoutingSessionInfo systemSessionInfo = null;
synchronized (mLock) {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
- List<RoutingSessionInfo> sessionInfos =
- userRecord.mHandler.mSystemProvider.getSessionInfos();
- if (sessionInfos != null && !sessionInfos.isEmpty()) {
- systemSessionInfo = sessionInfos.get(0);
+ List<RoutingSessionInfo> sessionInfos;
+ if (hasModifyAudioRoutingPermission) {
+ sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos();
+ if (sessionInfos != null && !sessionInfos.isEmpty()) {
+ systemSessionInfo = sessionInfos.get(0);
+ } else {
+ Slog.w(TAG, "System provider does not have any session info.");
+ }
} else {
- Slog.w(TAG, "System provider does not have any session info.");
+ systemSessionInfo = userRecord.mHandler.mSystemProvider.getDefaultSessionInfo();
}
}
return systemSessionInfo;
@@ -654,10 +669,20 @@ class MediaRouter2ServiceImpl {
return;
}
- routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::transferToRouteOnHandler,
- routerRecord.mUserRecord.mHandler,
- DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route));
+ String defaultRouteId =
+ routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId();
+ if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission
+ && !TextUtils.equals(route.getId(), defaultRouteId)) {
+ routerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::notifySessionCreationFailedToRouter,
+ routerRecord.mUserRecord.mHandler,
+ routerRecord, toOriginalRequestId(DUMMY_REQUEST_ID)));
+ } else {
+ routerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::transferToRouteOnHandler,
+ routerRecord.mUserRecord.mHandler,
+ DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route));
+ }
}
private void setSessionVolumeWithRouter2Locked(@NonNull IMediaRouter2 router,
@@ -1185,18 +1210,42 @@ class MediaRouter2ServiceImpl {
}
}
- List<IMediaRouter2> routers = getRouters();
+ List<IMediaRouter2> routersWithModifyAudioRoutingPermission = getRouters(true);
+ List<IMediaRouter2> routersWithoutModifyAudioRoutingPermission = getRouters(false);
List<IMediaRouter2Manager> managers = getManagers();
+ List<MediaRoute2Info> defaultRoute = new ArrayList<>();
+ defaultRoute.add(mSystemProvider.getDefaultRoute());
+
if (addedRoutes.size() > 0) {
- notifyRoutesAddedToRouters(routers, addedRoutes);
+ notifyRoutesAddedToRouters(routersWithModifyAudioRoutingPermission, addedRoutes);
+ if (!provider.mIsSystemRouteProvider) {
+ notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission,
+ addedRoutes);
+ } else if (prevInfo == null) {
+ notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission,
+ defaultRoute);
+ } // 'else' is handled as changed routes
notifyRoutesAddedToManagers(managers, addedRoutes);
}
if (removedRoutes.size() > 0) {
- notifyRoutesRemovedToRouters(routers, removedRoutes);
+ notifyRoutesRemovedToRouters(routersWithModifyAudioRoutingPermission,
+ removedRoutes);
+ if (!provider.mIsSystemRouteProvider) {
+ notifyRoutesRemovedToRouters(routersWithoutModifyAudioRoutingPermission,
+ removedRoutes);
+ }
notifyRoutesRemovedToManagers(managers, removedRoutes);
}
if (changedRoutes.size() > 0) {
- notifyRoutesChangedToRouters(routers, changedRoutes);
+ notifyRoutesChangedToRouters(routersWithModifyAudioRoutingPermission,
+ changedRoutes);
+ if (!provider.mIsSystemRouteProvider) {
+ notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission,
+ changedRoutes);
+ } else if (prevInfo != null) {
+ notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission,
+ defaultRoute);
+ } // 'else' is handled as added routes
notifyRoutesChangedToManagers(managers, changedRoutes);
}
}
@@ -1223,6 +1272,15 @@ class MediaRouter2ServiceImpl {
toOriginalRequestId(uniqueRequestId));
return;
}
+ if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission
+ && !TextUtils.equals(route.getId(),
+ mSystemProvider.getDefaultRoute().getId())) {
+ Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to"
+ + route);
+ notifySessionCreationFailedToRouter(routerRecord,
+ toOriginalRequestId(uniqueRequestId));
+ return;
+ }
SessionCreationRequest request =
new SessionCreationRequest(routerRecord, uniqueRequestId, route, managerRecord);
@@ -1444,7 +1502,9 @@ class MediaRouter2ServiceImpl {
if (service == null) {
return;
}
- notifySessionInfoChangedToRouters(getRouters(), sessionInfo);
+ notifySessionInfoChangedToRouters(getRouters(true), sessionInfo);
+ notifySessionInfoChangedToRouters(getRouters(false),
+ mSystemProvider.getDefaultSessionInfo());
return;
}
@@ -1565,7 +1625,7 @@ class MediaRouter2ServiceImpl {
}
}
- private List<IMediaRouter2> getRouters() {
+ private List<IMediaRouter2> getAllRouters() {
final List<IMediaRouter2> routers = new ArrayList<>();
MediaRouter2ServiceImpl service = mServiceRef.get();
if (service == null) {
@@ -1579,6 +1639,23 @@ class MediaRouter2ServiceImpl {
return routers;
}
+ private List<IMediaRouter2> getRouters(boolean hasModifyAudioRoutingPermission) {
+ final List<IMediaRouter2> routers = new ArrayList<>();
+ MediaRouter2ServiceImpl service = mServiceRef.get();
+ if (service == null) {
+ return routers;
+ }
+ synchronized (service.mLock) {
+ for (RouterRecord routerRecord : mUserRecord.mRouterRecords) {
+ if (hasModifyAudioRoutingPermission
+ == routerRecord.mHasModifyAudioRoutingPermission) {
+ routers.add(routerRecord.mRouter);
+ }
+ }
+ }
+ return routers;
+ }
+
private List<IMediaRouter2Manager> getManagers() {
final List<IMediaRouter2Manager> managers = new ArrayList<>();
MediaRouter2ServiceImpl service = mServiceRef.get();
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 5b16d686e04c..6e2feeb15e21 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -72,6 +72,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
// This should be the currently selected route.
MediaRoute2Info mDefaultRoute;
MediaRoute2Info mDeviceRoute;
+ RoutingSessionInfo mDefaultSessionInfo;
final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
@@ -114,6 +115,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
});
updateSessionInfosIfNeeded();
+
mContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
@@ -156,6 +158,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
@Override
public void transferToRoute(long requestId, String sessionId, String routeId) {
+ if (TextUtils.equals(routeId, DEFAULT_ROUTE_ID)) {
+ // The currently selected route is the default route.
+ return;
+ }
if (mBtRouteProvider != null) {
if (TextUtils.equals(routeId, mDeviceRoute.getId())) {
mBtRouteProvider.transferTo(null);
@@ -182,6 +188,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
return mDefaultRoute;
}
+ public RoutingSessionInfo getDefaultSessionInfo() {
+ return mDefaultSessionInfo;
+ }
+
private void updateDeviceRoute(AudioRoutesInfo newRoutes) {
int name = R.string.default_audio_route_name;
if (newRoutes != null) {
@@ -229,8 +239,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
*/
boolean updateSessionInfosIfNeeded() {
synchronized (mLock) {
- // Prevent to execute this method before mBtRouteProvider is created.
- if (mBtRouteProvider == null) return false;
RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(
0);
@@ -238,14 +246,19 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
SYSTEM_SESSION_ID, "" /* clientPackageName */)
.setSystemSession(true);
- MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute();
- if (selectedRoute == null) {
- selectedRoute = mDeviceRoute;
- } else {
- builder.addTransferableRoute(mDeviceRoute.getId());
+ MediaRoute2Info selectedRoute = mDeviceRoute;
+ if (mBtRouteProvider != null) {
+ MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute();
+ if (selectedBtRoute != null) {
+ selectedRoute = selectedBtRoute;
+ builder.addTransferableRoute(mDeviceRoute.getId());
+ }
}
mSelectedRouteId = selectedRoute.getId();
- mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute).build();
+ mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute)
+ .setSystemRoute(true)
+ .setProviderId(mUniqueId)
+ .build();
builder.addSelectedRoute(mSelectedRouteId);
for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
@@ -258,6 +271,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
} else {
mSessionInfos.clear();
mSessionInfos.add(newSessionInfo);
+ mDefaultSessionInfo = new RoutingSessionInfo.Builder(
+ SYSTEM_SESSION_ID, "" /* clientPackageName */)
+ .setProviderId(mUniqueId)
+ .setSystemSession(true)
+ .addSelectedRoute(DEFAULT_ROUTE_ID)
+ .build();
return true;
}
}
@@ -302,6 +321,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
} else if (mBtRouteProvider != null) {
mBtRouteProvider.setSelectedRouteVolume(newVolume);
}
+ mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute)
+ .setVolume(newVolume)
+ .build();
publishProviderState();
}
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 1e4dc7bfe778..daf4bf271ca2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -399,7 +399,9 @@ public abstract class ApexManager {
@VisibleForTesting
protected IApexService waitForApexService() {
try {
- return IApexService.Stub.asInterface(Binder.waitForService("apexservice"));
+ // Since apexd is a trusted platform component, synchronized calls are allowable
+ return IApexService.Stub.asInterface(
+ Binder.allowBlocking(Binder.waitForService("apexservice")));
} catch (RemoteException e) {
throw new IllegalStateException("Required service apexservice not available");
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index ba7583fe7f7a..dab4bfd4df5a 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -46,6 +46,7 @@ import com.android.server.pm.dex.DexoptOptions;
import java.io.File;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -106,6 +107,8 @@ public class BackgroundDexOptService extends JobService {
private static final long mDowngradeUnusedAppsThresholdInMillis =
getDowngradeUnusedAppsThresholdInMillis();
+ private static List<PackagesUpdatedListener> sPackagesUpdatedListeners = new ArrayList<>();
+
public static void schedule(Context context) {
if (isBackgroundDexoptDisabled()) {
return;
@@ -244,6 +247,7 @@ public class BackgroundDexOptService extends JobService {
}
}
notifyPinService(updatedPackages);
+ notifyPackagesUpdated(updatedPackages);
// Ran to completion, so we abandon our timeslice and do not reschedule.
jobFinished(jobParams, /* reschedule */ false);
}
@@ -391,6 +395,7 @@ public class BackgroundDexOptService extends JobService {
} finally {
// Always let the pinner service know about changes.
notifyPinService(updatedPackages);
+ notifyPackagesUpdated(updatedPackages);
}
}
@@ -642,6 +647,32 @@ public class BackgroundDexOptService extends JobService {
}
}
+ public static interface PackagesUpdatedListener {
+ /** Callback when packages have been updated by the bg-dexopt service. */
+ public void onPackagesUpdated(ArraySet<String> updatedPackages);
+ }
+
+ public static void addPackagesUpdatedListener(PackagesUpdatedListener listener) {
+ synchronized (sPackagesUpdatedListeners) {
+ sPackagesUpdatedListeners.add(listener);
+ }
+ }
+
+ public static void removePackagesUpdatedListener(PackagesUpdatedListener listener) {
+ synchronized (sPackagesUpdatedListeners) {
+ sPackagesUpdatedListeners.remove(listener);
+ }
+ }
+
+ /** Notify all listeners (#addPackagesUpdatedListener) that packages have been updated. */
+ private void notifyPackagesUpdated(ArraySet<String> updatedPackages) {
+ synchronized (sPackagesUpdatedListeners) {
+ for (PackagesUpdatedListener listener : sPackagesUpdatedListeners) {
+ listener.onPackagesUpdated(updatedPackages);
+ }
+ }
+ }
+
private static long getDowngradeUnusedAppsThresholdInMillis() {
final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
String sysPropValue = SystemProperties.get(sysPropKey);
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index e7d0c41c0fea..65b7cf3eabd1 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -51,6 +51,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.os.storage.StorageManager;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -79,7 +80,7 @@ import java.util.Map;
* Helper class for running dexopt command on packages.
*/
public class PackageDexOptimizer {
- private static final String TAG = "PackageManager.DexOptimizer";
+ private static final String TAG = "PackageDexOptimizer";
static final String OAT_DIR_NAME = "oat";
// TODO b/19550105 Remove error codes and use exceptions
public static final int DEX_OPT_SKIPPED = 0;
@@ -307,6 +308,55 @@ public class PackageDexOptimizer {
}
}
+ /**
+ * Perform dexopt (if needed) on a system server code path).
+ */
+ public int dexoptSystemServerPath(
+ String dexPath, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
+ int dexoptFlags = DEXOPT_PUBLIC
+ | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
+ | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
+
+ int result = DEX_OPT_SKIPPED;
+ for (String isa : dexUseInfo.getLoaderIsas()) {
+ int dexoptNeeded = getDexoptNeeded(
+ dexPath,
+ isa,
+ options.getCompilerFilter(),
+ dexUseInfo.getClassLoaderContext(),
+ /* newProfile= */false,
+ /* downgrade= */ false);
+
+ if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
+ continue;
+ }
+ try {
+ mInstaller.dexopt(
+ dexPath,
+ android.os.Process.SYSTEM_UID,
+ /* packageName= */ "android",
+ isa,
+ dexoptNeeded,
+ /* oatDir= */ null,
+ dexoptFlags,
+ options.getCompilerFilter(),
+ StorageManager.UUID_PRIVATE_INTERNAL,
+ dexUseInfo.getClassLoaderContext(),
+ /* seInfo= */ null,
+ /* downgrade= */ false ,
+ /* targetSdk= */ 0,
+ /* profileName */ null,
+ /* dexMetadataPath */ null,
+ getReasonName(options.getCompilationReason()));
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to dexopt", e);
+ return DEX_OPT_FAILED;
+ }
+ result = DEX_OPT_PERFORMED;
+ }
+ return result;
+ }
+
private String getAugmentedReasonName(int compilationReason, boolean useDexMetadata) {
String annotation = useDexMetadata
? ArtManagerService.DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION : "";
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f07fa501d3a8..2221644bff47 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -128,6 +128,7 @@ import com.android.internal.content.PackageHelper;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
@@ -1801,6 +1802,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ private void logDataLoaderInstallationSession(int returnCode, String extraMessage) {
+ final long currentTimestamp = System.currentTimeMillis();
+ FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED,
+ isIncrementalInstallation(),
+ mPackageName,
+ currentTimestamp - createdMillis,
+ returnCode);
+ }
+
/**
* Returns true if the session should attempt to inherit any existing native libraries already
* extracted at the current install location. This is necessary to prevent double loading of
@@ -2789,6 +2799,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
mCallback.onSessionFinished(this, success);
+ if (isDataLoaderInstallation()) {
+ logDataLoaderInstallationSession(returnCode, msg);
+ }
}
/** {@hide} */
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f693555d9761..9c41e6d5c6a3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9855,6 +9855,11 @@ public class PackageManagerService extends IPackageManager.Stub
private int performDexOptInternalWithDependenciesLI(AndroidPackage p,
@NonNull PackageSetting pkgSetting, DexoptOptions options) {
+ // System server gets a special path.
+ if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) {
+ return mDexManager.dexoptSystemServer(options);
+ }
+
// Select the dex optimizer based on the force parameter.
// Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
// allocate an object here.
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index ebdf85691e58..eb7057dd2994 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -49,6 +49,8 @@ import dalvik.system.VMRuntime;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -437,15 +439,7 @@ public class DexManager {
* because they don't need to be compiled)..
*/
public boolean dexoptSecondaryDex(DexoptOptions options) {
- // Select the dex optimizer based on the force parameter.
- // Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust
- // the necessary dexopt flags to make sure that compilation is not skipped. This avoid
- // passing the force flag through the multitude of layers.
- // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
- // allocate an object here.
- PackageDexOptimizer pdo = options.isForce()
- ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
- : mPackageDexOptimizer;
+ PackageDexOptimizer pdo = getPackageDexOptimizer(options);
String packageName = options.getPackageName();
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
if (useInfo.getDexUseInfoMap().isEmpty()) {
@@ -486,6 +480,83 @@ public class DexManager {
}
/**
+ * Performs dexopt on system server dex files.
+ *
+ * <p>Verfifies that the package name is {@link PackageManagerService#PLATFORM_PACKAGE_NAME}.
+ *
+ * @return
+ * <p>PackageDexOptimizer.DEX_OPT_SKIPPED if dexopt was skipped because no system server
+ * files were recorded or if no dexopt was needed.
+ * <p>PackageDexOptimizer.DEX_OPT_FAILED if any dexopt operation failed.
+ * <p>PackageDexOptimizer.DEX_OPT_PERFORMED if all dexopt operations succeeded.
+ */
+ public int dexoptSystemServer(DexoptOptions options) {
+ if (!PLATFORM_PACKAGE_NAME.equals(options.getPackageName())) {
+ Slog.wtf(TAG, "Non system server package used when trying to dexopt system server:"
+ + options.getPackageName());
+ return PackageDexOptimizer.DEX_OPT_FAILED;
+ }
+
+ PackageDexOptimizer pdo = getPackageDexOptimizer(options);
+ String packageName = options.getPackageName();
+ PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
+ if (useInfo.getDexUseInfoMap().isEmpty()) {
+ if (DEBUG) {
+ Slog.d(TAG, "No dex files recorded for system server");
+ }
+ // Nothing to compile, return true.
+ return PackageDexOptimizer.DEX_OPT_SKIPPED;
+ }
+
+ boolean usageUpdated = false;
+ int result = PackageDexOptimizer.DEX_OPT_SKIPPED;
+ for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) {
+ String dexPath = entry.getKey();
+ DexUseInfo dexUseInfo = entry.getValue();
+ if (!Files.exists(Paths.get(dexPath))) {
+ if (DEBUG) {
+ Slog.w(TAG, "A dex file previously loaded by System Server does not exist "
+ + " anymore: " + dexPath);
+ }
+ usageUpdated = mPackageDexUsage.removeDexFile(
+ packageName, dexPath, dexUseInfo.getOwnerUserId()) || usageUpdated;
+ continue;
+ }
+
+ int newResult = pdo.dexoptSystemServerPath(dexPath, dexUseInfo, options);
+
+ // The end result is:
+ // - FAILED if any path failed,
+ // - PERFORMED if at least one path needed compilation,
+ // - SKIPPED when all paths are up to date
+ if ((result != PackageDexOptimizer.DEX_OPT_FAILED)
+ && (newResult != PackageDexOptimizer.DEX_OPT_SKIPPED)) {
+ result = newResult;
+ }
+ }
+
+ if (usageUpdated) {
+ mPackageDexUsage.maybeWriteAsync();
+ }
+
+ return result;
+ }
+
+ /**
+ * Select the dex optimizer based on the force parameter.
+ * Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust
+ * the necessary dexopt flags to make sure that compilation is not skipped. This avoid
+ * passing the force flag through the multitude of layers.
+ * Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
+ * allocate an object here.
+ */
+ private PackageDexOptimizer getPackageDexOptimizer(DexoptOptions options) {
+ return options.isForce()
+ ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
+ : mPackageDexOptimizer;
+ }
+
+ /**
* Reconcile the information we have about the secondary dex files belonging to
* {@code packagName} and the actual dex files. For all dex files that were
* deleted, update the internal records and delete any generated oat files.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index cd6c5bfaeb79..5a3464d8a35f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3219,6 +3219,10 @@ public final class PowerManagerService extends SystemService
private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
@Nullable final String reason, boolean wait) {
if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
+ if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
+ throw new UnsupportedOperationException(
+ "Attempted userspace reboot on a device that doesn't support it");
+ }
UserspaceRebootLogger.noteUserspaceRebootWasRequested();
}
if (mHandler == null || !mSystemReady) {
diff --git a/services/core/java/com/android/server/textclassifier/IconsContentProvider.java b/services/core/java/com/android/server/textclassifier/IconsContentProvider.java
new file mode 100644
index 000000000000..d19a707770e2
--- /dev/null
+++ b/services/core/java/com/android/server/textclassifier/IconsContentProvider.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.textclassifier;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.textclassifier.IconsUriHelper.ResourceInfo;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A content provider that is used to access icons returned from the TextClassifier service.
+ *
+ * <p>Use {@link IconsUriHelper#getContentUri(String, int)} to access a uri for a specific resource.
+ * The uri may be passed to other processes to access the specified resource.
+ *
+ * <p>NOTE: Care must be taken to avoid leaking resources to non-permitted apps via this provider.
+ */
+public final class IconsContentProvider extends ContentProvider {
+
+ private static final String TAG = "IconsContentProvider";
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) {
+ try {
+ final ResourceInfo res = IconsUriHelper.getInstance().getResourceInfo(uri);
+ final Drawable drawable = Icon.createWithResource(res.packageName, res.id)
+ .loadDrawable(getContext());
+ final byte[] data = getBitmapData(drawable);
+ final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ final ParcelFileDescriptor readSide = pipe[0];
+ final ParcelFileDescriptor writeSide = pipe[1];
+ try (OutputStream out = new AutoCloseOutputStream(writeSide)) {
+ out.write(data);
+ return readSide;
+ }
+ } catch (IOException | RuntimeException e) {
+ Log.e(TAG, "Error retrieving icon for uri: " + uri, e);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the bitmap data for the specified drawable.
+ */
+ @VisibleForTesting
+ public static byte[] getBitmapData(Drawable drawable) {
+ if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
+ throw new IllegalStateException("The icon is zero-sized");
+ }
+
+ final Bitmap bitmap = Bitmap.createBitmap(
+ drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+
+ final Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+
+ final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
+ final byte[] byteArray = stream.toByteArray();
+ bitmap.recycle();
+ return byteArray;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return "image/png";
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/services/core/java/com/android/server/textclassifier/IconsUriHelper.java b/services/core/java/com/android/server/textclassifier/IconsUriHelper.java
new file mode 100644
index 000000000000..f17b0f14bd0e
--- /dev/null
+++ b/services/core/java/com/android/server/textclassifier/IconsUriHelper.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.textclassifier;
+
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.function.Supplier;
+
+/**
+ * A helper for mapping an icon resource to a content uri.
+ *
+ * <p>NOTE: Care must be taken to avoid passing resource uris to non-permitted apps via this helper.
+ */
+@VisibleForTesting(visibility = Visibility.PACKAGE)
+public final class IconsUriHelper {
+
+ public static final String AUTHORITY = "com.android.textclassifier.icons";
+
+ private static final String TAG = "IconsUriHelper";
+ private static final Supplier<String> DEFAULT_ID_SUPPLIER = () -> UUID.randomUUID().toString();
+
+ // TODO: Consider using an LRU cache to limit resource usage.
+ // This may depend on the expected number of packages that a device typically has.
+ @GuardedBy("mPackageIds")
+ private final Map<String, String> mPackageIds = new ArrayMap<>();
+
+ private final Supplier<String> mIdSupplier;
+
+ private static final IconsUriHelper sSingleton = new IconsUriHelper(null);
+
+ private IconsUriHelper(@Nullable Supplier<String> idSupplier) {
+ mIdSupplier = (idSupplier != null) ? idSupplier : DEFAULT_ID_SUPPLIER;
+
+ // Useful for testing:
+ // Magic id for the android package so it is the same across classloaders.
+ // This is okay as this package does not have access restrictions, and
+ // the TextClassifierService hardly returns icons from this package.
+ mPackageIds.put("android", "android");
+ }
+
+ /**
+ * Returns a new instance of this object for testing purposes.
+ */
+ public static IconsUriHelper newInstanceForTesting(@Nullable Supplier<String> idSupplier) {
+ return new IconsUriHelper(idSupplier);
+ }
+
+ static IconsUriHelper getInstance() {
+ return sSingleton;
+ }
+
+ /**
+ * Returns a Uri for the specified icon resource.
+ *
+ * @param packageName the resource's package name
+ * @param resId the resource id
+ * @see #getResourceInfo(Uri)
+ */
+ public Uri getContentUri(String packageName, int resId) {
+ Objects.requireNonNull(packageName);
+ synchronized (mPackageIds) {
+ if (!mPackageIds.containsKey(packageName)) {
+ // TODO: Ignore packages that don't actually exist on the device.
+ mPackageIds.put(packageName, mIdSupplier.get());
+ }
+ return new Uri.Builder()
+ .scheme("content")
+ .authority(AUTHORITY)
+ .path(mPackageIds.get(packageName))
+ .appendPath(Integer.toString(resId))
+ .build();
+ }
+ }
+
+ /**
+ * Returns a valid {@link ResourceInfo} for the specified uri. Returns {@code null} if a valid
+ * {@link ResourceInfo} cannot be returned for the specified uri.
+ *
+ * @see #getContentUri(String, int);
+ */
+ @Nullable
+ public ResourceInfo getResourceInfo(Uri uri) {
+ if (!"content".equals(uri.getScheme())) {
+ return null;
+ }
+ if (!AUTHORITY.equals(uri.getAuthority())) {
+ return null;
+ }
+
+ final List<String> pathItems = uri.getPathSegments();
+ try {
+ synchronized (mPackageIds) {
+ final String packageId = pathItems.get(0);
+ final int resId = Integer.parseInt(pathItems.get(1));
+ for (String packageName : mPackageIds.keySet()) {
+ if (packageId.equals(mPackageIds.get(packageName))) {
+ return new ResourceInfo(packageName, resId);
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.v(TAG, "Could not get resource info. Reason: " + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * A holder for a resource's package name and id.
+ */
+ public static final class ResourceInfo {
+
+ public final String packageName;
+ public final int id;
+
+ private ResourceInfo(String packageName, int id) {
+ this.packageName = Objects.requireNonNull(packageName);
+ this.id = id;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4e2b745ff409..856fbc70438e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6490,11 +6490,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Rect containingBounds = mTmpBounds;
mCompatDisplayInsets.getContainerBounds(containingAppBounds, containingBounds, rotation,
orientation, orientationRequested, canChangeOrientation);
- resolvedBounds.set(containingAppBounds);
+ resolvedBounds.set(containingBounds);
// The size of floating task is fixed (only swap), so the aspect ratio is already correct.
if (!mCompatDisplayInsets.mIsFloating) {
applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds);
}
+ // If the bounds are restricted by fixed aspect ratio, the resolved bounds should be put in
+ // the container app bounds. Otherwise the entire container bounds are available.
+ final boolean fillContainer = resolvedBounds.equals(containingBounds);
+ if (!fillContainer) {
+ // The horizontal position should not cover insets.
+ resolvedBounds.left = containingAppBounds.left;
+ }
// Use resolvedBounds to compute other override configurations such as appBounds. The bounds
// are calculated in compat container space. The actual position on screen will be applied
@@ -6563,7 +6570,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int offsetX = getHorizontalCenterOffset(
(int) viewportW, (int) (contentW * mSizeCompatScale));
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
- final int screenPosX = parentAppBounds.left + offsetX;
+ final int screenPosX = (fillContainer ? parentBounds.left : parentAppBounds.left) + offsetX;
final int screenPosY = parentBounds.top;
if (screenPosX != 0 || screenPosY != 0) {
if (mSizeCompatBounds != null) {
@@ -7660,8 +7667,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Ensure the app bounds won't overlap with insets.
Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]);
}
- // The horizontal position is centered and it should not cover insets.
- outBounds.left = outAppBounds.left;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 1e5a924e1d4d..0470ffa6cdca 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1469,7 +1469,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
if (toDisplay.getDisplayId() != stack.getDisplayId()) {
stack.reparent(toDisplay.getDefaultTaskDisplayArea(), false /* onTop */);
} else {
- toDisplay.mTaskContainers.positionStackAtBottom(stack);
+ toDisplay.getDefaultTaskDisplayArea().positionStackAtBottom(stack);
}
mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index d777f3fee1a2..4e85837e9616 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
@@ -191,8 +192,9 @@ public class ActivityStartController {
try {
// TODO(multi-display-area): Support starting home in a task display area
// Make sure home stack exist on display.
+ // TODO(b/153624902): Replace with TaskDisplayArea#getOrCreateRootHomeTask()
homeStack = display.getDefaultTaskDisplayArea().getOrCreateStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, ON_TOP);
} finally {
mSupervisor.endDeferResume();
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index edd14b7bebf3..a512337c96ec 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -54,7 +54,6 @@ import java.util.function.Predicate;
* - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks.
* - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks.
* - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container.
- * Cannot have a sibling that is of type ANY.
*
* @param <T> type of the children of the DisplayArea.
*/
@@ -274,7 +273,6 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
ANY;
static void checkSiblings(Type bottom, Type top) {
- checkState(!(bottom == ANY && top == ANY), "ANY cannot be a sibling of ANY");
checkState(!(bottom != BELOW_TASKS && top == BELOW_TASKS),
bottom + " must be above BELOW_TASKS");
checkState(!(bottom == ABOVE_TASKS && top != ABOVE_TASKS),
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 982157336295..15b6f9735bf1 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -16,9 +16,14 @@
package com.android.server.wm;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+
import android.content.res.Resources;
import android.text.TextUtils;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Policy that manages DisplayAreas.
*/
@@ -37,9 +42,9 @@ public abstract class DisplayAreaPolicy {
protected final DisplayArea<? extends WindowContainer> mImeContainer;
/**
- * The Tasks container. Tasks etc. are automatically added to this container.
+ * The task display areas. Tasks etc. are automatically added to these containers.
*/
- protected final DisplayArea<? extends ActivityStack> mTaskContainers;
+ protected final List<TaskDisplayArea> mTaskDisplayAreas;
/**
* Construct a new {@link DisplayAreaPolicy}
@@ -48,19 +53,19 @@ public abstract class DisplayAreaPolicy {
* @param content the display content for which the policy applies
* @param root the root display area under which the policy operates
* @param imeContainer the ime container that the policy must attach
- * @param taskDisplayArea the task container that the policy must attach
+ * @param taskDisplayAreas the task display areas that the policy must attach
*
* @see #attachDisplayAreas()
*/
protected DisplayAreaPolicy(WindowManagerService wmService,
DisplayContent content, DisplayArea.Root root,
DisplayArea<? extends WindowContainer> imeContainer,
- DisplayArea<? extends ActivityStack> taskDisplayArea) {
+ List<TaskDisplayArea> taskDisplayAreas) {
mWmService = wmService;
mContent = content;
mRoot = root;
mImeContainer = imeContainer;
- mTaskContainers = taskDisplayArea;
+ mTaskDisplayAreas = taskDisplayAreas;
}
/**
@@ -80,15 +85,32 @@ public abstract class DisplayAreaPolicy {
*/
public abstract void addWindow(WindowToken token);
+ /**
+ * @return the number of task display areas on the display.
+ */
+ public int getTaskDisplayAreaCount() {
+ return mTaskDisplayAreas.size();
+ }
+
+ /**
+ * @return the task display area at index.
+ */
+ public TaskDisplayArea getTaskDisplayAreaAt(int index) {
+ return mTaskDisplayAreas.get(index);
+ }
+
/** Provider for platform-default display area policy. */
static final class DefaultProvider implements DisplayAreaPolicy.Provider {
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, DisplayArea.Root root,
- DisplayArea<? extends WindowContainer> imeContainer,
- TaskDisplayArea taskDisplayArea) {
+ DisplayArea<? extends WindowContainer> imeContainer) {
+ final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
+ "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
+ final List<TaskDisplayArea> tdaList = new ArrayList<>();
+ tdaList.add(defaultTaskDisplayArea);
return new DisplayAreaPolicyBuilder()
- .build(wmService, content, root, imeContainer, taskDisplayArea);
+ .build(wmService, content, root, imeContainer, tdaList);
}
}
@@ -107,8 +129,7 @@ public abstract class DisplayAreaPolicy {
*/
DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, DisplayArea.Root root,
- DisplayArea<? extends WindowContainer> imeContainer,
- TaskDisplayArea taskDisplayArea);
+ DisplayArea<? extends WindowContainer> imeContainer);
/**
* Instantiate the device-specific {@link Provider}.
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index e8becfa27fac..2c2f43392eca 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -201,8 +201,8 @@ class DisplayAreaPolicyBuilder {
Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root,
DisplayArea<? extends WindowContainer> imeContainer,
- DisplayArea<? extends ActivityStack> taskDisplayArea, ArrayList<Feature> features) {
- super(wmService, content, root, imeContainer, taskDisplayArea);
+ List<TaskDisplayArea> taskDisplayAreas, ArrayList<Feature> features) {
+ super(wmService, content, root, imeContainer, taskDisplayAreas);
mFeatures = features;
mAreas = new HashMap<>(features.size());
for (int i = 0; i < mFeatures.size(); i++) {
@@ -267,7 +267,7 @@ class DisplayAreaPolicyBuilder {
areaForLayer[layer].mChildren.add(leafArea);
leafType = type;
if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
- leafArea.mExisting = mTaskContainers;
+ addTaskDisplayAreasToLayer(areaForLayer[layer], layer);
} else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
leafArea.mExisting = mImeContainer;
}
@@ -278,6 +278,17 @@ class DisplayAreaPolicyBuilder {
root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas);
}
+ /** Adds all task display areas to the specified layer */
+ private void addTaskDisplayAreasToLayer(PendingArea parentPendingArea, int layer) {
+ final int count = mTaskDisplayAreas.size();
+ for (int i = 0; i < count; i++) {
+ PendingArea leafArea = new PendingArea(null, layer, parentPendingArea);
+ leafArea.mExisting = mTaskDisplayAreas.get(i);
+ leafArea.mMaxLayer = layer;
+ parentPendingArea.mChildren.add(leafArea);
+ }
+ }
+
@Override
public void addWindow(WindowToken token) {
DisplayArea.Tokens area = findAreaForToken(token);
@@ -317,12 +328,16 @@ class DisplayAreaPolicyBuilder {
return this;
}
+ protected List<Feature> getFeatures() {
+ return mFeatures;
+ }
+
Result build(WindowManagerService wmService,
DisplayContent content, DisplayArea.Root root,
DisplayArea<? extends WindowContainer> imeContainer,
- DisplayArea<? extends ActivityStack> taskDisplayArea) {
+ List<TaskDisplayArea> taskDisplayAreas) {
- return new Result(wmService, content, root, imeContainer, taskDisplayArea, new ArrayList<>(
+ return new Result(wmService, content, root, imeContainer, taskDisplayAreas, new ArrayList<>(
mFeatures));
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2816633045b9..40243e8bbedf 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -72,6 +72,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
@@ -268,8 +269,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
new NonAppWindowContainers("mOverlayContainers", mWmService);
/** The containers below are the only child containers {@link #mWindowContainers} can have. */
- // Contains all window containers that are related to apps (Activities)
- final TaskDisplayArea mTaskContainers = new TaskDisplayArea(this, mWmService);
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
@@ -971,7 +970,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
super.addChild(mOverlayContainers, null);
mDisplayAreaPolicy = mWmService.mDisplayAreaPolicyProvider.instantiate(
- mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers);
+ mWmService, this, mRootDisplayArea, mImeWindowsContainers);
mWindowContainers.addChildren();
// Sets the display content for the children.
@@ -2069,13 +2068,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
protected int getTaskDisplayAreaCount() {
- // TODO(multi-display-area): Report actual display area count
- return 1;
+ return mDisplayAreaPolicy.getTaskDisplayAreaCount();
}
protected TaskDisplayArea getTaskDisplayAreaAt(int index) {
- // TODO(multi-display-area): Report actual display area values
- return mTaskContainers;
+ return mDisplayAreaPolicy.getTaskDisplayAreaAt(index);
}
ActivityStack getStack(int rootTaskId) {
@@ -2401,7 +2398,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* or for cases when multi-instance is not supported yet (like Split-screen, PiP or Recents).
*/
TaskDisplayArea getDefaultTaskDisplayArea() {
- return mTaskContainers;
+ return mDisplayAreaPolicy.getTaskDisplayAreaAt(0);
}
@Override
@@ -3424,7 +3421,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) {
// Always update control target. This is needed to handle rotation.
- updateImeControlTarget(target);
+ // We cannot set target as the control target, because mInputMethodTarget can only help
+ // decide the z-order of IME, but cannot control IME. Only the IME target reported from
+ // updateInputMethodTargetWindow can control IME.
+ updateImeControlTarget(mInputMethodControlTarget);
if (target == mInputMethodTarget && mInputMethodTargetWaitingAnim == targetWaitingAnim) {
return;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 42cd66e4e964..11fda369ec5f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -1370,8 +1369,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
calculateDefaultMinimalSizeOfResizeableTasks();
final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
- defaultTaskDisplayArea.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME,
- ON_TOP);
+ defaultTaskDisplayArea.getOrCreateRootHomeTask();
positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
false /* includingParents */);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 50a0ea837df3..1e70573a2ada 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2334,32 +2334,30 @@ class Task extends WindowContainer<WindowContainer> {
return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
}
- private void resolveOrganizedOverrideConfiguration(Configuration newParentConfig) {
+ @Override
+ void resolveOverrideConfiguration(Configuration newParentConfig) {
+ mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
super.resolveOverrideConfiguration(newParentConfig);
- if (!isOrganized()) {
- return;
- }
- final Task root = getRootTask();
- if (root == this) {
- return;
+ // Resolve override windowing mode to fullscreen for home task (even on freeform
+ // display), or split-screen-secondary if in split-screen mode.
+ int windowingMode =
+ getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+ if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = inSplitScreenWindowingMode() ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ : WINDOWING_MODE_FULLSCREEN;
+ getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
}
- // Ensure to have the same windowing mode for the child tasks that controlled by task org.
- getResolvedOverrideConfiguration().windowConfiguration
- .setWindowingMode(root.getWindowingMode());
- }
-
- @Override
- void resolveOverrideConfiguration(Configuration newParentConfig) {
- if (!isLeafTask() || mCreatedByOrganizer) {
- resolveOrganizedOverrideConfiguration(newParentConfig);
+ if (!isLeafTask()) {
+ // Compute configuration overrides for tasks that created by organizer, so that
+ // organizer can get the correct configuration from those tasks.
+ if (mCreatedByOrganizer) {
+ computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
+ }
return;
}
- mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
- resolveOrganizedOverrideConfiguration(newParentConfig);
- int windowingMode =
- getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index ec3c99bf0808..fee1c8019a07 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -31,7 +31,6 @@ import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.window.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
@@ -141,8 +140,9 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
*/
private boolean mRemoved;
- TaskDisplayArea(DisplayContent displayContent, WindowManagerService service) {
- super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER);
+ TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
+ int displayAreaFeature) {
+ super(service, Type.ANY, name, displayAreaFeature);
mDisplayContent = displayContent;
mRootWindowContainer = service.mRoot;
mAtmService = service.mAtmService;
@@ -1415,8 +1415,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
ActivityStack homeTask = getRootHomeTask();
if (homeTask == null && mDisplayContent.supportsSystemDecorations()
&& !mDisplayContent.isUntrustedVirtualDisplay()) {
- homeTask = createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME,
- false /* onTop */);
+ homeTask = createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, false /* onTop */);
}
return homeTask;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d0def3d0644d..ecc07bd4de01 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2582,4 +2582,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return willSync;
}
+
+ boolean useBLASTSync() {
+ return mUsingBLASTSyncTransaction;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8d3a4f27a257..c3fb68f9e0c4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -32,6 +32,7 @@ import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -72,6 +73,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
@@ -2109,6 +2111,10 @@ public class WindowManagerService extends IWindowManager.Stub
win.finishSeamlessRotation(false /* timeout */);
}
+ if (win.useBLASTSync()) {
+ result |= RELAYOUT_RES_BLAST_SYNC;
+ }
+
int attrChanges = 0;
int flagChanges = 0;
int privateFlagChanges = 0;
@@ -2573,6 +2579,8 @@ public class WindowManagerService extends IWindowManager.Stub
String packageName, boolean fromClientToken) {
final boolean callerCanManageAppTokens =
checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
+ // WindowContext users usually don't hold MANAGE_APP_TOKEN permission. Check permissions
+ // by checkAddPermission.
if (!callerCanManageAppTokens) {
final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */,
packageName, new int[1]);
@@ -2587,7 +2595,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
if (!callerCanManageAppTokens) {
if (packageName == null || !unprivilegedAppCanCreateTokenWith(
- null /* parentWindow */, callingUid, type, type, null /* tokenForLog */,
+ null /* parentWindow */, callingUid, type, type, binder,
packageName)) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
@@ -2612,7 +2620,7 @@ public class WindowManagerService extends IWindowManager.Stub
new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
} else {
new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens,
- false /* roundedCornerOverlay */, fromClientToken);
+ callingUid, false /* roundedCornerOverlay */, fromClientToken);
}
}
} finally {
@@ -2635,8 +2643,25 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void removeWindowToken(IBinder binder, int displayId) {
- if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+ final boolean callerCanManageAppTokens =
+ checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()");
+ final WindowToken windowToken;
+ synchronized (mGlobalLock) {
+ windowToken = mRoot.getWindowToken(binder);
+ }
+ if (windowToken == null) {
+ ProtoLog.w(WM_ERROR,
+ "removeWindowToken: Attempted to remove non-existing token: %s", binder);
+ return;
+ }
+ final int callingUid = Binder.getCallingUid();
+
+ // If MANAGE_APP_TOKEN permission is not held(usually from WindowContext), callers can only
+ // remove the window tokens which they added themselves.
+ if (!callerCanManageAppTokens && (windowToken.getOwnerUid() == INVALID_UID
+ || callingUid != windowToken.getOwnerUid())) {
+ throw new SecurityException("removeWindowToken: Requires MANAGE_APP_TOKENS permission"
+ + " to remove token owned by another uid");
}
final long origId = Binder.clearCallingIdentity();
@@ -2649,14 +2674,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- final WindowToken token = dc.removeWindowToken(binder);
- if (token == null) {
- ProtoLog.w(WM_ERROR,
- "removeWindowToken: Attempted to remove non-existing token: %s",
- binder);
- return;
- }
-
+ dc.removeWindowToken(binder);
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
}
} finally {
@@ -8006,6 +8024,33 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ /** Set layer tracing flags. */
+ public void setLayerTracingFlags(int flags) {
+ mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP,
+ "setLayerTracingFlags");
+ long token = Binder.clearCallingIdentity();
+ try {
+ Parcel data = null;
+ try {
+ IBinder sf = ServiceManager.getService("SurfaceFlinger");
+ if (sf != null) {
+ data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ data.writeInt(flags);
+ sf.transact(1033 /* LAYER_TRACE_FLAGS_CODE */, data, null, 0 /* flags */);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set layer tracing flags");
+ } finally {
+ if (data != null) {
+ data.recycle();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override
public boolean mirrorDisplay(int displayId, SurfaceControl outSurfaceControl) {
if (!checkCallingPermission(READ_FRAME_BUFFER, "mirrorDisplay()")) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 23091a00a80b..5976b48db764 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.os.Process.INVALID_UID;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -44,6 +45,7 @@ import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.InsetsState;
@@ -106,6 +108,11 @@ class WindowToken extends WindowContainer<WindowState> {
@VisibleForTesting
final boolean mFromClientToken;
+ private DeathRecipient mDeathRecipient;
+ private boolean mBinderDied = false;
+
+ private final int mOwnerUid;
+
/**
* Used to fix the transform of the token to be rotated to a rotation different than it's
* display. The window frames and surfaces corresponding to this token will be layouted and
@@ -165,6 +172,30 @@ class WindowToken extends WindowContainer<WindowState> {
}
}
+ private class DeathRecipient implements IBinder.DeathRecipient {
+ private boolean mHasUnlinkToDeath = false;
+
+ @Override
+ public void binderDied() {
+ synchronized (mWmService.mGlobalLock) {
+ mBinderDied = true;
+ removeImmediately();
+ }
+ }
+
+ void linkToDeath() throws RemoteException {
+ token.linkToDeath(DeathRecipient.this, 0);
+ }
+
+ void unlinkToDeath() {
+ if (mHasUnlinkToDeath) {
+ return;
+ }
+ token.unlinkToDeath(DeathRecipient.this, 0);
+ mHasUnlinkToDeath = true;
+ }
+ }
+
/**
* Compares two child window of this token and returns -1 if the first is lesser than the
* second in terms of z-order and 1 otherwise.
@@ -193,23 +224,35 @@ class WindowToken extends WindowContainer<WindowState> {
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
- this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
+ this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, INVALID_UID,
roundedCornerOverlay, false /* fromClientToken */);
}
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
- DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay,
- boolean fromClientToken) {
+ DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
+ boolean roundedCornerOverlay, boolean fromClientToken) {
super(service);
token = _token;
windowType = type;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
+ mOwnerUid = ownerUid;
mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken;
if (dc != null) {
dc.addWindowToken(token, this);
}
+ if (shouldReportToClient()) {
+ try {
+ mDeathRecipient = new DeathRecipient();
+ mDeathRecipient.linkToDeath();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to add window token with type " + windowType + " on "
+ + "display " + dc.getDisplayId(), e);
+ mDeathRecipient = null;
+ return;
+ }
+ }
}
void removeAllWindowsIfPossible() {
@@ -222,7 +265,7 @@ class WindowToken extends WindowContainer<WindowState> {
}
void setExiting() {
- if (mChildren.size() == 0) {
+ if (isEmpty()) {
super.removeImmediately();
return;
}
@@ -340,6 +383,21 @@ class WindowToken extends WindowContainer<WindowState> {
// Needs to occur after the token is removed from the display above to avoid attempt at
// duplicate removal of this window container from it's parent.
super.removeImmediately();
+
+ reportWindowTokenRemovedToClient();
+ }
+
+ private void reportWindowTokenRemovedToClient() {
+ if (!shouldReportToClient()) {
+ return;
+ }
+ mDeathRecipient.unlinkToDeath();
+ IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
+ try {
+ windowTokenClient.onWindowTokenRemoved();
+ } catch (RemoteException e) {
+ ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
+ }
}
@Override
@@ -361,17 +419,9 @@ class WindowToken extends WindowContainer<WindowState> {
}
void reportConfigToWindowTokenClient() {
- if (asActivityRecord() != null) {
- // Activities are updated through ATM callbacks.
+ if (!shouldReportToClient()) {
return;
}
-
- // Unfortunately, this WindowToken is not from WindowContext so it cannot handle
- // its own configuration changes.
- if (!mFromClientToken) {
- return;
- }
-
final Configuration config = getConfiguration();
final int displayId = getDisplayContent().getDisplayId();
if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) {
@@ -383,16 +433,26 @@ class WindowToken extends WindowContainer<WindowState> {
mLastReportedDisplay = displayId;
IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
- if (windowTokenClient != null) {
- try {
- windowTokenClient.onConfigurationChanged(config, displayId);
- } catch (RemoteException e) {
- ProtoLog.w(WM_ERROR,
- "Could not report config changes to the window token client.");
- }
+ try {
+ windowTokenClient.onConfigurationChanged(config, displayId);
+ } catch (RemoteException e) {
+ ProtoLog.w(WM_ERROR,
+ "Could not report config changes to the window token client.");
}
}
+ /**
+ * @return {@code true} if this {@link WindowToken} is not an {@link ActivityRecord} and
+ * registered from client side.
+ */
+ private boolean shouldReportToClient() {
+ // Only report to client for WindowToken because Activities are updated through ATM
+ // callbacks.
+ return asActivityRecord() == null
+ // Report to {@link android.view.WindowTokenClient} if this token was registered from it.
+ && mFromClientToken && !mBinderDied;
+ }
+
@Override
void assignLayer(SurfaceControl.Transaction t, int layer) {
if (windowType == TYPE_DOCK_DIVIDER) {
@@ -616,4 +676,8 @@ class WindowToken extends WindowContainer<WindowState> {
int getWindowLayerFromType() {
return mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, mOwnerCanManageAppTokens);
}
+
+ int getOwnerUid() {
+ return mOwnerUid;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 67e83bad154f..1da074002456 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1836,8 +1836,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Bundle addSyntheticRestrictions(Bundle restrictions) {
if (disableCamera) {
restrictions.putBoolean(UserManager.DISALLOW_CAMERA, true);
- } else {
- restrictions.remove(UserManager.DISALLOW_CAMERA);
+ }
+ if (requireAutoTime) {
+ restrictions.putBoolean(UserManager.DISALLOW_CONFIG_DATE_TIME, true);
}
return restrictions;
}
@@ -1864,7 +1865,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Bundle getEffectiveRestrictions() {
return addSyntheticRestrictions(
- removeDeprecatedRestrictions(ensureUserRestrictions()));
+ removeDeprecatedRestrictions(new Bundle(ensureUserRestrictions())));
}
Bundle getLocalUserRestrictions(int adminType) {
@@ -2701,10 +2702,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Slog.i(LOG_TAG, "Clearing the DO...");
final ComponentName doAdminReceiver = doAdmin.info.getComponent();
clearDeviceOwnerLocked(doAdmin, doUserId);
- // TODO(b/143516163): If we have a power cut here, we might leave active admin. Consider if
- // it is worth the complexity to make it more robust.
Slog.i(LOG_TAG, "Removing admin artifacts...");
- // TODO(b/143516163): Clean up application restrictions in UserManager.
+ // TODO(b/149075700): Clean up application restrictions in UserManager.
removeAdminArtifacts(doAdminReceiver, doUserId);
Slog.i(LOG_TAG, "Migration complete.");
@@ -2746,16 +2745,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// The following policies weren't available to PO, but will be available after migration.
parentAdmin.disableCamera = doAdmin.disableCamera;
-
- // TODO(b/143516163): Uncomment once corresponding APIs are available via parent instance.
- // parentAdmin.disableScreenCapture = doAdmin.disableScreenCapture;
- // parentAdmin.accountTypesWithManagementDisabled.addAll(
- // doAdmin.accountTypesWithManagementDisabled);
+ parentAdmin.requireAutoTime = doAdmin.requireAutoTime;
+ parentAdmin.disableScreenCapture = doAdmin.disableScreenCapture;
+ parentAdmin.accountTypesWithManagementDisabled.addAll(
+ doAdmin.accountTypesWithManagementDisabled);
moveDoUserRestrictionsToCopeParent(doAdmin, parentAdmin);
-
- // TODO(b/143516163): migrate network and security logging state, currently they are
- // turned off when DO is removed.
}
private void moveDoUserRestrictionsToCopeParent(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) {
@@ -2775,7 +2770,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* a managed profile.
*/
@GuardedBy("getLockObject()")
- void applyManagedProfileRestrictionIfDeviceOwnerLocked() {
+ private void applyManagedProfileRestrictionIfDeviceOwnerLocked() {
final int doUserId = mOwners.getDeviceOwnerUserId();
if (doUserId == UserHandle.USER_NULL) {
logIfVerbose("No DO found, skipping application of restriction.");
@@ -3999,11 +3994,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mOwners.systemReady();
break;
case SystemService.PHASE_ACTIVITY_MANAGER_READY:
- maybeStartSecurityLogMonitorOnActivityManagerReady();
synchronized (getLockObject()) {
migrateToProfileOnOrganizationOwnedDeviceIfCompLocked();
applyManagedProfileRestrictionIfDeviceOwnerLocked();
}
+ maybeStartSecurityLogMonitorOnActivityManagerReady();
final int userId = getManagedUserId(UserHandle.USER_SYSTEM);
if (userId >= 0) {
updatePersonalAppSuspension(userId, false /* running */);
@@ -7839,16 +7834,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final int userHandle = UserHandle.getCallingUserId();
+ boolean requireAutoTimeChanged = false;
synchronized (getLockObject()) {
ActiveAdmin admin = getActiveAdminForCallerLocked(who,
DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
if (admin.requireAutoTime != required) {
admin.requireAutoTime = required;
saveSettingsLocked(userHandle);
+ requireAutoTimeChanged = true;
}
}
-
- // TODO: (b/145604635) Add upgrade case
+ // requireAutoTime is now backed by DISALLOW_CONFIG_DATE_TIME restriction, so propagate
+ // updated restrictions to the framework.
+ if (requireAutoTimeChanged) {
+ pushUserRestrictions(userHandle);
+ }
// Turn AUTO_TIME on in settings if it is required
if (required) {
mInjector.binderWithCleanCallingIdentity(
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index d1153e6cf6e7..92366e51eb47 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -51,7 +51,7 @@ using namespace android::content::pm;
namespace fs = std::filesystem;
constexpr const char* kDataUsageStats = "android.permission.LOADER_USAGE_STATS";
-constexpr const char* kOpUsage = "android:get_usage_stats";
+constexpr const char* kOpUsage = "android:loader_usage_stats";
namespace android::incremental {
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 0635ae169281..18ae4b5af435 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -137,6 +137,7 @@ public:
bool* _aidl_return) {
mId = mountId;
mListener = listener;
+ mServiceConnector = control.service;
*_aidl_return = true;
return binder::Status::ok();
}
@@ -166,9 +167,17 @@ public:
mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_CREATED);
}
+ int32_t setStorageParams(bool enableReadLogs) {
+ int32_t result = -1;
+ EXPECT_NE(mServiceConnector.get(), nullptr);
+ EXPECT_TRUE(mServiceConnector->setStorageParams(enableReadLogs, &result).isOk());
+ return result;
+ }
+
private:
int mId;
sp<IDataLoaderStatusListener> mListener;
+ sp<IIncrementalServiceConnector> mServiceConnector;
sp<IDataLoader> mDataLoader = sp<IDataLoader>(new FakeDataLoader());
};
@@ -453,7 +462,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) {
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
- ASSERT_GE(mIncrementalService->setStorageParams(storageId, true), 0);
+ ASSERT_GE(mDataLoaderManager->setStorageParams(true), 0);
}
TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) {
@@ -480,7 +489,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChang
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
- ASSERT_GE(mIncrementalService->setStorageParams(storageId, true), 0);
+ ASSERT_GE(mDataLoaderManager->setStorageParams(true), 0);
ASSERT_NE(nullptr, mAppOpsManager->mStoredCallback.get());
mAppOpsManager->mStoredCallback->opChanged(0, {});
}
@@ -503,7 +512,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionFails) {
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
- ASSERT_LT(mIncrementalService->setStorageParams(storageId, true), 0);
+ ASSERT_LT(mDataLoaderManager->setStorageParams(true), 0);
}
TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) {
@@ -526,7 +535,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) {
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
- ASSERT_LT(mIncrementalService->setStorageParams(storageId, true), 0);
+ ASSERT_LT(mDataLoaderManager->setStorageParams(true), 0);
}
TEST_F(IncrementalServiceTest, testMakeDirectory) {
diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java b/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java
index c225d3feb063..1aab6722dfee 100644
--- a/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java
+++ b/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.systemcaptions;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
@@ -37,6 +39,8 @@ final class RemoteSystemCaptionsManagerService {
private static final String SERVICE_INTERFACE =
"android.service.systemcaptions.SystemCaptionsManagerService";
+ private static final int MSG_BIND = 1;
+
private final Object mLock = new Object();
private final Context mContext;
@@ -71,18 +75,26 @@ final class RemoteSystemCaptionsManagerService {
if (mVerbose) {
Slog.v(TAG, "initialize()");
}
- ensureBound();
+ scheduleBind();
+ }
+
+ /**
+ * Destroys this service.
+ */
+ public void destroy() {
+ mHandler.sendMessage(
+ obtainMessage(RemoteSystemCaptionsManagerService::handleDestroy, this));
}
- void destroy() {
+ void handleDestroy() {
if (mVerbose) {
- Slog.v(TAG, "destroy()");
+ Slog.v(TAG, "handleDestroy()");
}
synchronized (mLock) {
if (mDestroyed) {
if (mVerbose) {
- Slog.v(TAG, "destroy(): Already destroyed");
+ Slog.v(TAG, "handleDestroy(): Already destroyed");
}
return;
}
@@ -97,14 +109,24 @@ final class RemoteSystemCaptionsManagerService {
}
}
- private void ensureBound() {
+ private void scheduleBind() {
+ if (mHandler.hasMessages(MSG_BIND)) {
+ if (mVerbose) Slog.v(TAG, "scheduleBind(): already scheduled");
+ return;
+ }
+ mHandler.sendMessage(
+ obtainMessage(RemoteSystemCaptionsManagerService::handleEnsureBound, this)
+ .setWhat(MSG_BIND));
+ }
+
+ private void handleEnsureBound() {
synchronized (mLock) {
if (mService != null || mBinding) {
return;
}
if (mVerbose) {
- Slog.v(TAG, "ensureBound(): binding");
+ Slog.v(TAG, "handleEnsureBound(): binding");
}
mBinding = true;
diff --git a/services/tests/servicestests/res/raw/comp_policies_primary.xml b/services/tests/servicestests/res/raw/comp_policies_primary.xml
index d30f479195e3..395b8ab4ba75 100644
--- a/services/tests/servicestests/res/raw/comp_policies_primary.xml
+++ b/services/tests/servicestests/res/raw/comp_policies_primary.xml
@@ -3,6 +3,11 @@
<admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1">
<policies flags="991"/>
<password-history-length value="33" />
+ <require_auto_time value="true" />
<user-restrictions no_bluetooth="true" />
+ <disable-screen-capture value="true" />
+ <disable-account-management>
+ <account-type value="com.google-primary" />
+ </disable-account-management>
</admin>
</policies>
diff --git a/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml b/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml
index c874dcca2c73..c65d05693f10 100644
--- a/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml
+++ b/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml
@@ -2,5 +2,8 @@
<policies setup-complete="true" provisioning-state="3">
<admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1">
<policies flags="991"/>
+ <disable-account-management>
+ <account-type value="com.google-profile" />
+ </disable-account-management>
</admin>
</policies>
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index de2addffa2c5..74e7f8c44d1a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -19,6 +19,7 @@ import static android.os.UserHandle.USER_SYSTEM;
import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile;
+import static org.junit.Assert.assertArrayEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -378,6 +379,15 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
33, dpm.getParentProfileInstance(admin1).getPasswordHistoryLength(admin1));
assertEquals("Password history policy was put into non-parent PO instance",
0, dpm.getPasswordHistoryLength(admin1));
+ assertTrue("Screen capture restriction wasn't migrated to PO parent instance",
+ dpm.getParentProfileInstance(admin1).getScreenCaptureDisabled(admin1));
+
+ assertArrayEquals("Accounts with management disabled weren't migrated to PO parent",
+ new String[] {"com.google-primary"},
+ dpm.getParentProfileInstance(admin1).getAccountTypesWithManagementDisabled());
+ assertArrayEquals("Accounts with management disabled for profile were lost",
+ new String[] {"com.google-profile"},
+ dpm.getAccountTypesWithManagementDisabled());
assertTrue("User restriction wasn't migrated to PO parent instance",
dpm.getParentProfileInstance(admin1).getUserRestrictions(admin1)
@@ -385,7 +395,15 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
assertFalse("User restriction was put into non-parent PO instance",
dpm.getUserRestrictions(admin1).containsKey(UserManager.DISALLOW_BLUETOOTH));
- // TODO(b/143516163): verify more policies.
+ assertTrue("User restriction wasn't migrated to PO parent instance",
+ dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID)
+ .getParentActiveAdmin()
+ .getEffectiveRestrictions()
+ .containsKey(UserManager.DISALLOW_CONFIG_DATE_TIME));
+ assertFalse("User restriction was put into non-parent PO instance",
+ dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID)
+ .getEffectiveRestrictions()
+ .containsKey(UserManager.DISALLOW_CONFIG_DATE_TIME));
});
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index d780370b9849..fe224ce058f4 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2032,13 +2032,17 @@ public class DevicePolicyManagerTest extends DpmTestBase {
eq(false));
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(UserManager.DISALLOW_CAMERA),
- parentDpm.getUserRestrictions(admin1)
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .getParentActiveAdmin()
+ .getEffectiveRestrictions()
);
parentDpm.setCameraDisabled(admin1, false);
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(),
- parentDpm.getUserRestrictions(admin1)
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .getParentActiveAdmin()
+ .getEffectiveRestrictions()
);
reset(getServices().userManagerInternal);
}
@@ -2053,7 +2057,9 @@ public class DevicePolicyManagerTest extends DpmTestBase {
parentDpm.clearUserRestriction(admin1, restriction);
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(),
- parentDpm.getUserRestrictions(admin1)
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .getParentActiveAdmin()
+ .getEffectiveRestrictions()
);
}
@@ -2088,11 +2094,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
private void assertNoDeviceOwnerRestrictions() {
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(),
- getDeviceOwner().ensureUserRestrictions()
- );
- DpmTestUtils.assertRestrictions(
- DpmTestUtils.newRestrictions(),
- dpm.getUserRestrictions(admin1)
+ getDeviceOwner().getEffectiveRestrictions()
);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java b/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java
index 7c9a81d2e094..a525814435ea 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java
@@ -43,7 +43,9 @@ import java.lang.ref.WeakReference;
@RunWith(AndroidJUnit4.class)
public class AppSaturationControllerTest {
- private static final String TEST_PACKAGE_NAME = "com.android.test";
+ private static final String TEST_CALLER_PACKAGE_NAME = "com.android.test.caller";
+ private static final String TEST_CALLER_PACKAGE_NAME_TWO = "com.android.test.caller.two";
+ private static final String TEST_AFFECTED_PACKAGE_NAME = "com.android.test.affected";
private int mUserId;
private AppSaturationController mAppSaturationController;
@@ -70,8 +72,11 @@ public class AppSaturationControllerTest {
public void addColorTransformController_appliesExistingSaturation() {
final WeakReference<ColorTransformController> ref = new WeakReference<>(
mColorTransformController);
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
- mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 30);
+ mAppSaturationController
+ .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
verify(mColorTransformController).applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
}
@@ -80,14 +85,19 @@ public class AppSaturationControllerTest {
public void setSaturationLevel_resetToDefault() {
final WeakReference<ColorTransformController> ref = new WeakReference<>(
mColorTransformController);
- mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+ mAppSaturationController
+ .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
verify(mColorTransformController, never())
.applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 30);
AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
verify(mColorTransformController, times(1))
.applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 100);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 100);
AppSaturationController.computeGrayscaleTransformMatrix(1.0f, mMatrix);
verify(mColorTransformController, times(2))
.applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
@@ -97,19 +107,76 @@ public class AppSaturationControllerTest {
public void setSaturationLevel_updateLevel() {
final WeakReference<ColorTransformController> ref = new WeakReference<>(
mColorTransformController);
- mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+ mAppSaturationController
+ .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
verify(mColorTransformController, never())
.applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 30);
AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
verify(mColorTransformController).applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 70);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 70);
AppSaturationController.computeGrayscaleTransformMatrix(.7f, mMatrix);
verify(mColorTransformController, times(2))
.applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 100);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 100);
AppSaturationController.computeGrayscaleTransformMatrix(1.0f, mMatrix);
verify(mColorTransformController, times(3))
.applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
}
+
+ @Test
+ public void setSaturationLevel_multipleCallers_appliesStrongest() {
+ final WeakReference<ColorTransformController> ref = new WeakReference<>(
+ mColorTransformController);
+ mAppSaturationController
+ .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
+ verify(mColorTransformController, never())
+ .applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 30);
+ AppSaturationController.computeGrayscaleTransformMatrix(0.3f, mMatrix);
+ verify(mColorTransformController, times(1))
+ .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME_TWO, TEST_AFFECTED_PACKAGE_NAME,
+ mUserId,
+ 70);
+ verify(mColorTransformController, times(2))
+ .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+ }
+
+ @Test
+ public void setSaturationLevel_multipleCallers_removingOneDoesNotAffectTheOther() {
+ final WeakReference<ColorTransformController> ref = new WeakReference<>(
+ mColorTransformController);
+ mAppSaturationController
+ .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
+ verify(mColorTransformController, never())
+ .applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 70);
+ AppSaturationController.computeGrayscaleTransformMatrix(0.7f, mMatrix);
+ verify(mColorTransformController, times(1))
+ .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME_TWO, TEST_AFFECTED_PACKAGE_NAME,
+ mUserId,
+ 30);
+ AppSaturationController.computeGrayscaleTransformMatrix(0.3f, mMatrix);
+ verify(mColorTransformController, times(2))
+ .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME_TWO, TEST_AFFECTED_PACKAGE_NAME,
+ mUserId,
+ 100);
+ AppSaturationController.computeGrayscaleTransformMatrix(0.7f, mMatrix);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java b/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java
new file mode 100644
index 000000000000..72580a3b98c2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Sanity test for {@link IconsContentProvider}.
+ */
+@RunWith(AndroidJUnit4.class)
+public final class IconsContentProviderTest {
+
+ @Test
+ public void testLoadResource() {
+ final Context context = ApplicationProvider.getApplicationContext();
+ // Testing with the android package name because this is the only package name
+ // that returns the same uri across multiple classloaders.
+ final String packageName = "android";
+ final int resId = android.R.drawable.btn_star;
+ final Uri uri = IconsUriHelper.getInstance().getContentUri(packageName, resId);
+
+ final Drawable expected = Icon.createWithResource(packageName, resId).loadDrawable(context);
+ // Ensure we are testing with a non-empty image.
+ assertThat(expected.getIntrinsicWidth()).isGreaterThan(0);
+ assertThat(expected.getIntrinsicHeight()).isGreaterThan(0);
+
+ final Drawable actual = Icon.createWithContentUri(uri).loadDrawable(context);
+ assertThat(actual).isNotNull();
+ assertThat(IconsContentProvider.getBitmapData(actual))
+ .isEqualTo(IconsContentProvider.getBitmapData(expected));
+ }
+
+ @Test
+ public void testLoadResource_badUri() {
+ final Uri badUri = new Uri.Builder()
+ .scheme("content")
+ .authority(IconsUriHelper.AUTHORITY)
+ .path("badPackageId")
+ .appendPath("1234")
+ .build();
+
+ final Context context = ApplicationProvider.getApplicationContext();
+ assertThat(Icon.createWithContentUri(badUri).loadDrawable(context)).isNull();
+ }
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/textclassifier/IconsUriHelperTest.java b/services/tests/servicestests/src/com/android/server/textclassifier/IconsUriHelperTest.java
new file mode 100644
index 000000000000..96f09d965b13
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/textclassifier/IconsUriHelperTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.textclassifier.IconsUriHelper.ResourceInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link IconsUriHelper}.
+ */
+@RunWith(AndroidJUnit4.class)
+public final class IconsUriHelperTest {
+
+ private IconsUriHelper mIconsUriHelper;
+
+ @Before
+ public void setUp() {
+ mIconsUriHelper = IconsUriHelper.newInstanceForTesting(null);
+ }
+
+ @Test
+ public void testGetContentUri() {
+ final IconsUriHelper iconsUriHelper = IconsUriHelper.newInstanceForTesting(() -> "pkgId");
+ final Uri expected = new Uri.Builder()
+ .scheme("content")
+ .authority(IconsUriHelper.AUTHORITY)
+ .path("pkgId")
+ .appendPath("1234")
+ .build();
+
+ final Uri actual = iconsUriHelper.getContentUri("com.package.name", 1234);
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void testGetContentUri_multiplePackages() {
+ final Uri uri1 = mIconsUriHelper.getContentUri("com.package.name1", 1234);
+ final Uri uri2 = mIconsUriHelper.getContentUri("com.package.name2", 5678);
+
+ assertThat(uri1.getScheme()).isEqualTo("content");
+ assertThat(uri2.getScheme()).isEqualTo("content");
+
+ assertThat(uri1.getAuthority()).isEqualTo(IconsUriHelper.AUTHORITY);
+ assertThat(uri2.getAuthority()).isEqualTo(IconsUriHelper.AUTHORITY);
+
+ assertThat(uri1.getPathSegments().get(1)).isEqualTo("1234");
+ assertThat(uri2.getPathSegments().get(1)).isEqualTo("5678");
+ }
+
+ @Test
+ public void testGetContentUri_samePackageIdForSamePackageName() {
+ final String packageName = "com.package.name";
+ final Uri uri1 = mIconsUriHelper.getContentUri(packageName, 1234);
+ final Uri uri2 = mIconsUriHelper.getContentUri(packageName, 5678);
+
+ final String id1 = uri1.getPathSegments().get(0);
+ final String id2 = uri2.getPathSegments().get(0);
+
+ assertThat(id1).isEqualTo(id2);
+ }
+
+ @Test
+ public void testGetResourceInfo() {
+ mIconsUriHelper.getContentUri("com.package.name1", 123);
+ final Uri uri = mIconsUriHelper.getContentUri("com.package.name2", 456);
+ mIconsUriHelper.getContentUri("com.package.name3", 789);
+
+ final ResourceInfo res = mIconsUriHelper.getResourceInfo(uri);
+ assertThat(res.packageName).isEqualTo("com.package.name2");
+ assertThat(res.id).isEqualTo(456);
+ }
+
+ @Test
+ public void testGetResourceInfo_unrecognizedUri() {
+ final Uri uri = new Uri.Builder()
+ .scheme("content")
+ .authority(IconsUriHelper.AUTHORITY)
+ .path("unrecognized")
+ .appendPath("1234")
+ .build();
+ assertThat(mIconsUriHelper.getResourceInfo(uri)).isNull();
+ }
+
+ @Test
+ public void testGetResourceInfo_invalidScheme() {
+ final IconsUriHelper iconsUriHelper = IconsUriHelper.newInstanceForTesting(() -> "pkgId");
+ iconsUriHelper.getContentUri("com.package.name", 1234);
+
+ final Uri uri = new Uri.Builder()
+ .scheme("file")
+ .authority(IconsUriHelper.AUTHORITY)
+ .path("pkgId")
+ .appendPath("1234")
+ .build();
+ assertThat(iconsUriHelper.getResourceInfo(uri)).isNull();
+ }
+
+ @Test
+ public void testGetResourceInfo_invalidAuthority() {
+ final IconsUriHelper iconsUriHelper = IconsUriHelper.newInstanceForTesting(() -> "pkgId");
+ iconsUriHelper.getContentUri("com.package.name", 1234);
+
+ final Uri uri = new Uri.Builder()
+ .scheme("content")
+ .authority("invalid.authority")
+ .path("pkgId")
+ .appendPath("1234")
+ .build();
+ assertThat(iconsUriHelper.getResourceInfo(uri)).isNull();
+ }
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 327cfc74dac5..39062f017a73 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -818,6 +818,69 @@ public class AppStandbyControllerTests {
assertBucket(STANDBY_BUCKET_RESTRICTED);
}
+ /**
+ * Test that an app is "timed out" into the RESTRICTED bucket if prediction tries to put it into
+ * a low bucket after the RESTRICTED timeout.
+ */
+ @Test
+ public void testRestrictedTimeoutOverridesRestoredLowBucketPrediction() {
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ // Predict to RARE Not long enough to time out into RESTRICTED.
+ mInjector.mElapsedRealtime += RARE_THRESHOLD;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_PREDICTED);
+ assertBucket(STANDBY_BUCKET_RARE);
+
+ // Add a short timeout event
+ mInjector.mElapsedRealtime += 1000;
+ reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+ mInjector.mElapsedRealtime += 1000;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ // Long enough that it could have timed out into RESTRICTED. Instead of reverting to
+ // predicted RARE, should go into RESTRICTED
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+ // Ensure that prediction can still raise it out despite this override.
+ mInjector.mElapsedRealtime += 1;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_PREDICTED);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+ }
+
+ /**
+ * Test that an app is "timed out" into the RESTRICTED bucket if prediction tries to put it into
+ * a low bucket after the RESTRICTED timeout.
+ */
+ @Test
+ public void testRestrictedTimeoutOverridesPredictionLowBucket() {
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+
+ // Not long enough to time out into RESTRICTED.
+ mInjector.mElapsedRealtime += RARE_THRESHOLD;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_PREDICTED);
+ assertBucket(STANDBY_BUCKET_RARE);
+
+ mInjector.mElapsedRealtime += 1;
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+
+ // Long enough that it could have timed out into RESTRICTED.
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_PREDICTED);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_PREDICTED);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ }
+
@Test
public void testPredictionRaiseFromRestrictedTimeout_highBucket() {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 2077ecb2799e..96c69af036b6 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -197,7 +197,8 @@ public class ShortcutManagerTestUtils {
final String PREFIX = "Launcher: ComponentInfo{";
final String POSTFIX = "}";
final List<String> result = runShortcutCommandForSuccess(
- instrumentation, "get-default-launcher");
+ instrumentation, "get-default-launcher --user "
+ + instrumentation.getContext().getUserId());
for (String s : result) {
if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
return s.substring(PREFIX.length(), s.length() - POSTFIX.length());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 12bdec6ec1e1..0568be8d7fa6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.DisplayArea.Type.ANY;
@@ -72,7 +73,11 @@ public class DisplayAreaPolicyBuilderTest {
WindowManagerService wms = mSystemServices.getWindowManagerService();
DisplayArea.Root root = new SurfacelessDisplayAreaRoot(wms);
DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime");
- DisplayArea<ActivityStack> tasks = new DisplayArea<>(wms, ANY, "Tasks");
+ DisplayContent displayContent = mock(DisplayContent.class);
+ TaskDisplayArea taskDisplayArea = new TaskDisplayArea(displayContent, wms, "Tasks",
+ FEATURE_DEFAULT_TASK_CONTAINER);
+ List<TaskDisplayArea> taskDisplayAreaList = new ArrayList<>();
+ taskDisplayAreaList.add(taskDisplayArea);
final Feature foo;
final Feature bar;
@@ -86,7 +91,7 @@ public class DisplayAreaPolicyBuilderTest {
.all()
.except(TYPE_STATUS_BAR)
.build())
- .build(wms, mock(DisplayContent.class), root, ime, tasks);
+ .build(wms, displayContent, root, ime, taskDisplayAreaList);
policy.attachDisplayAreas();
@@ -98,9 +103,9 @@ public class DisplayAreaPolicyBuilderTest {
assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)),
is(not(decendantOfOneOf(policy.getDisplayAreas(bar)))));
- assertThat(tasks,
+ assertThat(taskDisplayArea,
is(decendantOfOneOf(policy.getDisplayAreas(foo))));
- assertThat(tasks,
+ assertThat(taskDisplayArea,
is(decendantOfOneOf(policy.getDisplayAreas(bar))));
assertThat(ime,
@@ -109,7 +114,8 @@ public class DisplayAreaPolicyBuilderTest {
is(decendantOfOneOf(policy.getDisplayAreas(bar))));
List<DisplayArea<?>> actualOrder = collectLeafAreas(root);
- Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime, tasks);
+ Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime,
+ taskDisplayArea);
actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList());
Map<DisplayArea<?>, Integer> expectedByMinLayer = mapValues(zSets,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
index 4e4627bf7e52..6834ee5f3950 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
@@ -77,8 +77,7 @@ public class DisplayAreaProviderTest {
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
- DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer,
- TaskDisplayArea taskDisplayArea) {
+ DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer) {
throw new RuntimeException("test stub");
}
}
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 618e6086b582..880c486c15af 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -54,17 +54,6 @@ public class DisplayAreaTest {
}
@Test
- public void testDisplayArea_positionChanged_throwsIfIncompatibleSibling() {
- WindowManagerService wms = mWmsRule.getWindowManagerService();
- DisplayArea<WindowContainer> parent = new SurfacelessDisplayArea<>(wms, ANY, "Parent");
- DisplayArea<WindowContainer> child1 = new DisplayArea<>(wms, ANY, "Child1");
- DisplayArea<WindowContainer> child2 = new DisplayArea<>(wms, ANY, "Child2");
-
- parent.addChild(child1, 0);
- assertThrows(IllegalStateException.class, () -> parent.addChild(child2, 0));
- }
-
- @Test
public void testType_typeOf() {
WindowManagerService wms = mWmsRule.getWindowManagerService();
@@ -87,10 +76,10 @@ public class DisplayAreaTest {
checkSiblings(BELOW_TASKS, ABOVE_TASKS);
checkSiblings(ANY, ABOVE_TASKS);
checkSiblings(ABOVE_TASKS, ABOVE_TASKS);
+ checkSiblings(ANY, ANY);
assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, BELOW_TASKS));
assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, ANY));
- assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, ANY));
assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, BELOW_TASKS));
}
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 673469474709..e47792f4920c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -243,6 +243,26 @@ public class SizeCompatTests extends ActivityTestsBase {
}
@Test
+ public void testAspectRatioMatchParentBoundsAndImeAttachable() {
+ setUpApp(new TestDisplayContent.Builder(mService, 1000, 2000)
+ .setSystemDecorations(true).build());
+ prepareUnresizable(2f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ assertFitted();
+
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+ mActivity.mDisplayContent.mInputMethodTarget = addWindowToActivity(mActivity);
+ // Because the aspect ratio of display doesn't exceed the max aspect ratio of activity.
+ // The activity should still fill its parent container and IME can attach to the activity.
+ assertTrue(mActivity.matchParentBounds());
+ assertTrue(mActivity.mDisplayContent.isImeAttachedToApp());
+
+ final Rect letterboxInnerBounds = new Rect();
+ mActivity.getLetterboxInnerBounds(letterboxInnerBounds);
+ // The activity should not have letterbox.
+ assertTrue(letterboxInnerBounds.isEmpty());
+ }
+
+ @Test
public void testMoveToDifferentOrientDisplay() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 9625ffdac052..a7085334bece 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -51,10 +51,10 @@ import org.junit.Test;
import org.junit.runner.RunWith;
/**
- * Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}.
+ * Tests for the {@link TaskDisplayArea} container.
*
* Build/Install/Run:
- * atest WmTests:TaskStackContainersTests
+ * atest WmTests:TaskDisplayAreaTests
*/
@SmallTest
@Presubmit
@@ -154,8 +154,9 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
ACTIVITY_TYPE_STANDARD, mDisplayContent);
final Task newStack = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, mDisplayContent);
- doReturn(newStack).when(mDisplayContent.mTaskContainers).createStack(anyInt(),
- anyInt(), anyBoolean(), any(), any(), anyBoolean());
+ final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
+ doReturn(newStack).when(taskDisplayArea).createStack(anyInt(), anyInt(), anyBoolean(),
+ any(), any(), anyBoolean());
final int type = ACTIVITY_TYPE_STANDARD;
assertGetOrCreateStack(WINDOWING_MODE_FULLSCREEN, type, candidateTask,
@@ -186,7 +187,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
private void assertGetOrCreateStack(int windowingMode, int activityType, Task candidateTask,
boolean reuseCandidate) {
- final TaskDisplayArea taskDisplayArea = (TaskDisplayArea) candidateTask.getParent();
+ final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
final ActivityStack stack = taskDisplayArea.getOrCreateStack(windowingMode, activityType,
false /* onTop */, null /* intent */, candidateTask /* candidateTask */);
assertEquals(reuseCandidate, stack == candidateTask);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 18737c2b4bb7..d2a2732d60ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -43,7 +43,9 @@ class TestDisplayContent extends DisplayContent {
// hard-code to FULLSCREEN for tests.
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
spyOn(this);
- spyOn(mTaskContainers);
+ for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+ spyOn(getTaskDisplayAreaAt(i));
+ }
final DisplayRotation displayRotation = getDisplayRotation();
spyOn(displayRotation);
@@ -137,6 +139,7 @@ class TestDisplayContent extends DisplayContent {
spyOn(displayPolicy);
if (mSystemDecorations) {
doReturn(true).when(newDisplay).supportsSystemDecorations();
+ doReturn(true).when(displayPolicy).hasNavigationBar();
} else {
doReturn(false).when(displayPolicy).hasNavigationBar();
doReturn(false).when(displayPolicy).hasStatusBar();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index da4bde59a09e..79b9ae1b902a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -16,13 +16,18 @@
package com.android.server.wm;
+import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
import android.os.IBinder;
@@ -85,4 +90,19 @@ public class WindowManagerServiceTests extends WindowTestsBase {
assertFalse(windowToken.mRoundedCornerOverlay);
assertTrue(windowToken.mFromClientToken);
}
+
+ @Test(expected = SecurityException.class)
+ public void testRemoveWindowToken_ownerUidNotMatch_throwException() {
+ IBinder token = mock(IBinder.class);
+ mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(),
+ null /* options */, null /* options */);
+
+ spyOn(mWm);
+ when(mWm.checkCallingPermission(anyString(), anyString())).thenReturn(false);
+ WindowToken windowToken = mWm.mRoot.getWindowToken(token);
+ spyOn(windowToken);
+ when(windowToken.getOwnerUid()).thenReturn(INVALID_UID);
+
+ mWm.removeWindowToken(token, mDisplayContent.getDisplayId());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 7a347cb050be..76479cb95b09 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -152,7 +153,7 @@ public class WindowTokenTests extends WindowTestsBase {
token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST,
true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */,
- true /* roundedCornerOverlay */, true /* fromClientToken */);
+ INVALID_UID, true /* roundedCornerOverlay */, true /* fromClientToken */);
assertTrue(token.mRoundedCornerOverlay);
assertTrue(token.mFromClientToken);
}
@@ -166,7 +167,7 @@ public class WindowTokenTests extends WindowTestsBase {
public void testSurfaceCreatedForWindowToken() {
final WindowToken fromClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_APPLICATION_OVERLAY, true /* persistOnEmpty */,
- mDisplayContent, true /* ownerCanManageAppTokens */,
+ mDisplayContent, true /* ownerCanManageAppTokens */, INVALID_UID,
true /* roundedCornerOverlay */, true /* fromClientToken */);
assertNull(fromClientToken.mSurfaceControl);
@@ -175,7 +176,7 @@ public class WindowTokenTests extends WindowTestsBase {
final WindowToken nonClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
false /* fromClientToken */);
assertNotNull(nonClientToken.mSurfaceControl);
}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index d5851d85345f..0c25cfb968fd 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -34,18 +34,21 @@ import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.pm.BackgroundDexOptService;
import com.android.server.wm.ActivityMetricsLaunchObserver;
import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
import com.android.server.wm.ActivityTaskManagerInternal;
+import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.HashMap;
@@ -286,6 +289,7 @@ public class IorapForwardingService extends SystemService {
private final AppLaunchObserver mAppLaunchObserver = new AppLaunchObserver();
private final EventSequenceValidator mEventSequenceValidator = new EventSequenceValidator();
+ private final DexOptPackagesUpdated mDexOptPackagesUpdated = new DexOptPackagesUpdated();
private boolean mRegisteredListeners = false;
private void registerInProcessListenersLocked() {
@@ -308,9 +312,22 @@ public class IorapForwardingService extends SystemService {
launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver);
launchObserverRegistry.registerLaunchObserver(mEventSequenceValidator);
+ BackgroundDexOptService.addPackagesUpdatedListener(mDexOptPackagesUpdated);
+
+
mRegisteredListeners = true;
}
+ private class DexOptPackagesUpdated implements BackgroundDexOptService.PackagesUpdatedListener {
+ @Override
+ public void onPackagesUpdated(ArraySet<String> updatedPackages) {
+ String[] updated = updatedPackages.toArray(new String[0]);
+ for (String packageName : updated) {
+ Log.d(TAG, "onPackagesUpdated: " + packageName);
+ }
+ }
+ }
+
private class AppLaunchObserver implements ActivityMetricsLaunchObserver {
// We add a synthetic sequence ID here to make it easier to differentiate new
// launch sequences on the native side.
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index f019a9d33005..bce06e4777a2 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -74,6 +74,7 @@ public abstract class Conference extends Conferenceable {
public void onConnectionEvent(Conference c, String event, Bundle extras) {}
public void onCallerDisplayNameChanged(
Conference c, String callerDisplayName, int presentation) {}
+ public void onCallDirectionChanged(Conference c, int callDirection) {}
public void onRingbackRequested(Conference c, boolean ringback) {}
}
@@ -103,6 +104,7 @@ public abstract class Conference extends Conferenceable {
private int mAddressPresentation;
private String mCallerDisplayName;
private int mCallerDisplayNamePresentation;
+ private int mCallDirection;
private boolean mRingbackRequested = false;
private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
@@ -1024,6 +1026,25 @@ public abstract class Conference extends Conferenceable {
}
/**
+ * Sets the call direction of this {@link Conference}. By default, all {@link Conference}s have
+ * a direction of {@link android.telecom.Call.Details.CallDirection#DIRECTION_UNKNOWN}. The
+ * direction of a {@link Conference} is only applicable to the case where
+ * {@link #setConferenceState(boolean)} has been set to {@code false}, otherwise the direction
+ * will be ignored.
+ * @param callDirection The direction of the conference.
+ * @hide
+ */
+ @RequiresPermission(MODIFY_PHONE_STATE)
+ public final void setCallDirection(@Call.Details.CallDirection int callDirection) {
+ Log.d(this, "setDirection %d", callDirection);
+ mCallDirection = callDirection;
+ for (Listener l : mListeners) {
+ l.onCallDirectionChanged(this, callDirection);
+ }
+ }
+
+
+ /**
* Sets the address of this {@link Conference}. Used when {@link #setConferenceState(boolean)}
* is called to mark a conference temporarily as NOT a conference.
* <p>
@@ -1102,6 +1123,15 @@ public abstract class Conference extends Conferenceable {
}
/**
+ * @return The call direction of this conference. Only applicable when
+ * {@link #setConferenceState(boolean)} is set to false.
+ * @hide
+ */
+ public final @Call.Details.CallDirection int getCallDirection() {
+ return mCallDirection;
+ }
+
+ /**
* Sets the caller display name (CNAP) of this {@link Conference}. Used when
* {@link #setConferenceState(boolean)} is called to mark a conference temporarily as NOT a
* conference.
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index ffd25c08a8ba..0d66013d92a1 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1554,6 +1554,14 @@ public abstract class ConnectionService extends Service {
}
@Override
+ public void onCallDirectionChanged(Conference c, int direction) {
+ String id = mIdByConference.get(c);
+ if (id != null) {
+ mAdapter.setCallDirection(id, direction);
+ }
+ }
+
+ @Override
public void onAddressChanged(Conference c, Uri newAddress, int presentation) {
String id = mIdByConference.get(c);
if (id != null) {
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 8f273233044e..f8a6cf03934a 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -693,4 +693,20 @@ final class ConnectionServiceAdapter implements DeathRecipient {
}
}
}
+
+ /**
+ * Sets the direction of a call. Setting a new direction of an existing call is usually only
+ * applicable during single caller emulation during conferencing, see
+ * {@link Conference#setConferenceState(boolean)} for more information.
+ * @param callId The identifier of the call.
+ * @param direction The new direction of the call.
+ */
+ void setCallDirection(String callId, @Call.Details.CallDirection int direction) {
+ for (IConnectionServiceAdapter a : mAdapters) {
+ try {
+ a.setCallDirection(callId, direction, Log.getExternalSession());
+ } catch (RemoteException e) {
+ }
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 79ad51b92b81..6c1ea322e66e 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -76,6 +76,7 @@ final class ConnectionServiceAdapterServant {
private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35;
private static final int MSG_SET_CONFERENCE_STATE = 36;
private static final int MSG_HANDLE_CREATE_CONFERENCE_COMPLETE = 37;
+ private static final int MSG_SET_CALL_DIRECTION = 38;
private final IConnectionServiceAdapter mDelegate;
@@ -353,7 +354,7 @@ final class ConnectionServiceAdapterServant {
case MSG_CONNECTION_SERVICE_FOCUS_RELEASED:
mDelegate.onConnectionServiceFocusReleased(null /*Session.Info*/);
break;
- case MSG_SET_CONFERENCE_STATE:
+ case MSG_SET_CONFERENCE_STATE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
mDelegate.setConferenceState((String) args.arg1, (Boolean) args.arg2,
@@ -361,6 +362,17 @@ final class ConnectionServiceAdapterServant {
} finally {
args.recycle();
}
+ break;
+ }
+ case MSG_SET_CALL_DIRECTION: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setCallDirection((String) args.arg1, args.argi1,
+ (Session.Info) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ }
}
}
};
@@ -670,6 +682,16 @@ final class ConnectionServiceAdapterServant {
args.arg3 = sessionInfo;
mHandler.obtainMessage(MSG_SET_CONFERENCE_STATE, args).sendToTarget();
}
+
+ @Override
+ public void setCallDirection(String callId, int direction,
+ Session.Info sessionInfo) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.argi1 = direction;
+ args.arg2 = sessionInfo;
+ mHandler.obtainMessage(MSG_SET_CALL_DIRECTION, args).sendToTarget();
+ }
};
public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 76640e036eeb..cad5b707a146 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -485,6 +485,11 @@ final class RemoteConnectionService {
Session.Info sessionInfo) {
// Do nothing
}
+
+ @Override
+ public void setCallDirection(String callId, int direction, Session.Info sessionInfo) {
+ // Do nothing
+ }
};
private final ConnectionServiceAdapterServant mServant =
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index c993cfad1d05..b974c567008f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1534,12 +1534,22 @@ public class TelecomManager {
/**
* Return the line 1 phone number for given phone account.
*
- * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_SMS READ_SMS},
+ * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+ * or that the caller is the default SMS app for any API level.
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * for apps targeting SDK API level 29 and below.
*
* @param accountHandle The handle for the account retrieve a number for.
* @return A string representation of the line 1 phone number.
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.READ_SMS,
+ android.Manifest.permission.READ_PHONE_NUMBERS
+ }, conditional = true)
public String getLine1Number(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 4f63e08abce6..3fd7f949cfe6 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -132,4 +132,6 @@ oneway interface IConnectionServiceAdapter {
void resetConnectionTime(String callIdi, in Session.Info sessionInfo);
void setConferenceState(String callId, boolean isConference, in Session.Info sessionInfo);
+
+ void setCallDirection(String callId, int direction, in Session.Info sessionInfo);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1a4b9d735977..56f3c3ec0622 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1178,6 +1178,25 @@ public class CarrierConfigManager {
"support_ims_conference_call_bool";
/**
+ * Determines whether the device will locally disconnect an IMS conference when the participant
+ * count drops to zero. When {@code true}, it is assumed the carrier does NOT disconnect a
+ * conference when the participant count drops to zero and that the device must do this by
+ * disconnecting the conference locally. When {@code false}, it is assumed that the carrier
+ * is responsible for disconnecting the conference when there are no longer any participants
+ * present.
+ * <p>
+ * Note: both {@link #KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL} and
+ * {@link #KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL} must be true for this configuration to
+ * have any effect.
+ * <p>
+ * Defaults to {@code false}, meaning the carrier network is responsible for disconnecting an
+ * empty IMS conference.
+ * @hide
+ */
+ public static final String KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL =
+ "local_disconnect_empty_ims_conference_bool";
+
+ /**
* Determines whether video conference calls are supported by a carrier. When {@code true},
* video calls can be merged into conference calls, {@code false} otherwiwse.
* <p>
@@ -3793,6 +3812,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL, true);
diff --git a/telephony/java/android/telephony/CbGeoUtils.java b/telephony/java/android/telephony/CbGeoUtils.java
index c0ae99e89cb3..806bac0c32c9 100644
--- a/telephony/java/android/telephony/CbGeoUtils.java
+++ b/telephony/java/android/telephony/CbGeoUtils.java
@@ -128,6 +128,23 @@ public class CbGeoUtils {
public String toString() {
return "(" + lat + "," + lng + ")";
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof LatLng)) {
+ return false;
+ }
+
+ LatLng l = (LatLng) o;
+ return lat == l.lat && lng == l.lng;
+ }
}
/**
@@ -280,6 +297,32 @@ public class CbGeoUtils {
}
return str;
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof Polygon)) {
+ return false;
+ }
+
+ Polygon p = (Polygon) o;
+ if (mVertices.size() != p.mVertices.size()) {
+ return false;
+ }
+ for (int i = 0; i < mVertices.size(); i++) {
+ if (!mVertices.get(i).equals(p.mVertices.get(i))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
}
/**
@@ -335,6 +378,24 @@ public class CbGeoUtils {
return str;
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof Circle)) {
+ return false;
+ }
+
+ Circle c = (Circle) o;
+ return mCenter.equals(c.mCenter)
+ && Double.compare(mRadiusMeter, c.mRadiusMeter) == 0;
+ }
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d6cdaa6d8bc0..f623649fb25e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -147,6 +147,10 @@ import java.util.regex.Pattern;
* information unless it has the appropriate permissions declared in
* its manifest file. Where permissions apply, they are noted in the
* the methods through which you access the protected information.
+ *
+ * <p>TelephonyManager is intended for use on devices that implement
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices
+ * that do not implement this feature, the behavior is not reliable.
*/
@SystemService(Context.TELEPHONY_SERVICE)
public class TelephonyManager {
@@ -5842,6 +5846,10 @@ public class TelephonyManager {
* {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()}
* for each active subscription.
*
+ * <p>This method returns valid data for devices with
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices
+ * that do not implement this feature, the behavior is not reliable.
+ *
* @param executor the executor on which callback will be invoked.
* @param callback a callback to receive CellInfo.
*/
@@ -5888,6 +5896,10 @@ public class TelephonyManager {
* {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()}
* for each active subscription.
*
+ * <p>This method returns valid data for devices with
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices
+ * that do not implement this feature, the behavior is not reliable.
+ *
* @param workSource the requestor to whom the power consumption for this should be attributed.
* @param executor the executor on which callback will be invoked.
* @param callback a callback to receive CellInfo.
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 13bf17954aa4..2d2f4dbdf907 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -107,7 +107,9 @@ public class AppLaunch extends InstrumentationTestCase {
private static final int PROFILE_SAVE_SLEEP_TIMEOUT = 1000; // Allow 1s for the profile to save
private static final int IORAP_TRACE_DURATION_TIMEOUT = 7000; // Allow 7s for trace to complete.
private static final int IORAP_TRIAL_LAUNCH_ITERATIONS = 3; // min 3 launches to merge traces.
- private static final int IORAP_COMPILE_CMD_TIMEOUT = 600; // in seconds: 10 minutes
+ private static final int IORAP_COMPILE_CMD_TIMEOUT = 60; // in seconds: 1 minutes
+ private static final int IORAP_COMPILE_MIN_TRACES = 1; // configure iorapd to need 1 trace.
+ private static final int IORAP_COMPILE_RETRIES = 3; // retry compiler 3 times if it fails.
private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
private static final String LAUNCH_FILE = "applaunch.txt";
private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
@@ -132,9 +134,9 @@ public class AppLaunch extends InstrumentationTestCase {
private static final String LAUNCH_ORDER_CYCLIC = "cyclic";
private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential";
private static final String COMPILE_CMD = "cmd package compile -f -m %s %s";
- private static final String IORAP_COMPILE_CMD = "cmd jobscheduler run -f android 283673059";
+ private static final String IORAP_COMPILE_CMD = "dumpsys iorapd --compile-package %s";
private static final String IORAP_MAINTENANCE_CMD =
- "iorap.cmd.maintenance --purge-package %s /data/misc/iorapd/sqlite.db";
+ "dumpsys iorapd --purge-package %s";
private static final String IORAP_DUMPSYS_CMD = "dumpsys iorapd";
private static final String SPEED_PROFILE_FILTER = "speed-profile";
private static final String VERIFY_FILTER = "verify";
@@ -350,9 +352,9 @@ public class AppLaunch extends InstrumentationTestCase {
sleep(IORAP_TRACE_DURATION_TIMEOUT);
if (launch.getLaunchReason().equals(IORAP_TRIAL_LAUNCH_LAST)) {
- // run the iorap job scheduler and wait for iorap to compile fully.
- assertTrue(String.format("Not able to iorap-compile the app : %s", appPkgName),
- compileAppForIorap(appPkgName));
+ // run the iorap compiler and wait for iorap to compile fully.
+ // this throws an exception if it fails.
+ compileAppForIorapWithRetries(appPkgName, IORAP_COMPILE_RETRIES);
}
}
@@ -506,6 +508,22 @@ public class AppLaunch extends InstrumentationTestCase {
}
/**
+ * Compile the app package using compilerFilter,
+ * retrying if the compilation command fails in between.
+ */
+ private void compileAppForIorapWithRetries(String appPkgName, int retries) throws IOException {
+ for (int i = 0; i < retries; ++i) {
+ if (compileAppForIorap(appPkgName)) {
+ return;
+ }
+ sleep(1000);
+ }
+
+ throw new IllegalStateException("compileAppForIorapWithRetries: timed out after "
+ + retries + " retries");
+ }
+
+ /**
* Compile the app package using compilerFilter and return true or false
* based on status of the compilation command.
*/
@@ -513,7 +531,7 @@ public class AppLaunch extends InstrumentationTestCase {
String logcatTimestamp = getTimeNowForLogcat();
getInstrumentation().getUiAutomation().
- executeShellCommand(IORAP_COMPILE_CMD);
+ executeShellCommand(String.format(IORAP_COMPILE_CMD, appPkgName));
int i = 0;
for (i = 0; i < IORAP_COMPILE_CMD_TIMEOUT; ++i) {
@@ -525,7 +543,8 @@ public class AppLaunch extends InstrumentationTestCase {
} else if (status == IorapCompilationStatus.INSUFFICIENT_TRACES) {
Log.e(TAG, "compileAppForIorap: failed due to insufficient traces");
logDumpsysIorapd(appPkgName);
- return false;
+ throw new IllegalStateException(
+ "compileAppForIorap: failed due to insufficient traces");
} // else INCOMPLETE. keep asking iorapd if it's done yet.
sleep(1000);
}
@@ -536,19 +555,7 @@ public class AppLaunch extends InstrumentationTestCase {
return false;
}
- // Wait for the job to finish completely.
- // Other packages could be compiled in cyclic runs.
- int currentAttempt = 0;
- do {
- String logcatLines = getLogcatSinceTime(logcatTimestamp);
- if (logcatLines.contains("IorapForwardingService: Finished background job")) {
- return true;
- }
- sleep(1000);
- } while (currentAttempt++ < IORAP_COMPILE_CMD_TIMEOUT);
-
- Log.e(TAG, "compileAppForIorap: failed due to jobscheduler timeout.");
- return false;
+ return true;
}
/** Save the contents of $(adb shell dumpsys iorapd) to the launch_logs directory. */
@@ -808,11 +815,9 @@ public class AppLaunch extends InstrumentationTestCase {
}
Log.v(TAG, "Purge iorap package: " + packageName);
- stopIorapd();
getInstrumentation().getUiAutomation()
.executeShellCommand(String.format(IORAP_MAINTENANCE_CMD, packageName));
Log.v(TAG, "Executed: " + String.format(IORAP_MAINTENANCE_CMD, packageName));
- startIorapd();
}
String executeShellCommandWithTempFile(String cmd) {
@@ -892,12 +897,16 @@ public class AppLaunch extends InstrumentationTestCase {
throw new AssertionError(e);
}
- stopIorapd();
getInstrumentation().getUiAutomation()
.executeShellCommand(String.format("setprop iorapd.perfetto.enable %b", enable));
getInstrumentation().getUiAutomation()
.executeShellCommand(String.format("setprop iorapd.readahead.enable %b", enable));
- startIorapd();
+ getInstrumentation().getUiAutomation()
+ .executeShellCommand(String.format(
+ "setprop iorapd.maintenance.min_traces %d", IORAP_COMPILE_MIN_TRACES));
+ // this last command blocks until iorapd refreshes its system properties
+ getInstrumentation().getUiAutomation()
+ .executeShellCommand(String.format("dumpsys iorapd --refresh-properties"));
if (enable) {
mIorapStatus = IorapStatus.ENABLED;
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
index b40d022f075d..86c3fa0fe034 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
@@ -53,8 +53,9 @@ public class TaskOrganizerMultiWindowTest extends Activity {
return true;
}
- float x = e.getX(0);
+ float x = e.getRawX(0);
float ratio = (float) x / (float) getWidth() ;
+ ratio = 1-ratio;
LinearLayout.LayoutParams lp1 =
new LinearLayout.LayoutParams(0,
@@ -172,10 +173,14 @@ public class TaskOrganizerMultiWindowTest extends Activity {
setContentView(splitView);
}
+ private void addFlags(Intent intent) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ }
+
Intent makeSettingsIntent() {
Intent intent = new Intent();
intent.setAction(android.provider.Settings.ACTION_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ addFlags(intent);
return intent;
}
@@ -183,7 +188,7 @@ public class TaskOrganizerMultiWindowTest extends Activity {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_APP_CONTACTS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ addFlags(intent);
return intent;
}
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java
index 03615f332723..aa041f22a46e 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java
@@ -80,8 +80,10 @@ class TaskView extends SurfaceView implements SurfaceHolder.Callback {
} catch (Exception e) {
// System server died.. oh well
}
+
t.reparent(leash, getSurfaceControl())
.setPosition(leash, 0, 0)
+ .show(leash)
.apply();
}
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 4c2a984f8198..737665fb97e4 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -17,6 +17,7 @@
package com.android.framework.permission.tests;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import android.os.Binder;
import android.os.RemoteException;
@@ -27,6 +28,8 @@ import android.view.IWindowManager;
import junit.framework.TestCase;
+import org.junit.Test;
+
/**
* TODO: Remove this. This is only a placeholder, need to implement this.
*/
@@ -53,7 +56,7 @@ public class WindowManagerPermissionTests extends TestCase {
}
try {
- mWm.addWindowToken(null, 0, DEFAULT_DISPLAY);
+ mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY);
fail("IWindowManager.addWindowToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -63,16 +66,6 @@ public class WindowManagerPermissionTests extends TestCase {
}
try {
- mWm.removeWindowToken(null, DEFAULT_DISPLAY);
- fail("IWindowManager.removeWindowToken did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
mWm.prepareAppTransition(0, false);
fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
+ " expected");
@@ -182,4 +175,29 @@ public class WindowManagerPermissionTests extends TestCase {
fail("Unexpected remote exception");
}
}
+
+ @Test
+ public void testADD_WINDOW_TOKEN_WITH_OPTIONS() {
+ // Verify if addWindowTokenWithOptions throw SecurityException for privileged window type.
+ try {
+ mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, "");
+ fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ } catch (RemoteException e) {
+ fail("Unexpected remote exception");
+ }
+
+ // Verify if addWindowTokenWithOptions throw SecurityException for null packageName.
+ try {
+ mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, null);
+ fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ } catch (RemoteException e) {
+ fail("Unexpected remote exception");
+ }
+ }
}
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index 3026e0b51133..0dd45bad8a01 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -47,7 +47,8 @@ public final class FrameworksTestsFilter extends SelectTest {
"android.view.InsetsSourceConsumerTest",
"android.view.InsetsStateTest",
"android.view.WindowMetricsTest",
- "android.view.PendingInsetsControllerTest"
+ "android.view.PendingInsetsControllerTest",
+ "android.app.WindowContextTest"
};
public FrameworksTestsFilter(Bundle testArgs) {
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
index 23a0f7278271..5fe94987aa65 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ b/tools/stats_log_api_gen/atoms_info_writer.cpp
@@ -42,7 +42,6 @@ static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) {
fprintf(out,
" const static std::set<int> "
"kTruncatingTimestampAtomBlackList;\n");
- fprintf(out, " const static std::map<int, int> kAtomsWithUidField;\n");
fprintf(out, " const static std::set<int> kAtomsWithAttributionChain;\n");
fprintf(out,
" const static std::map<int, StateAtomFieldOptions> "
@@ -101,28 +100,6 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
fprintf(out, "};\n");
fprintf(out, "\n");
- fprintf(out, "static std::map<int, int> getAtomUidField() {\n");
- fprintf(out, " std::map<int, int> uidField;\n");
- for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
- atomIt++) {
- if ((*atomIt)->uidField == 0) {
- continue;
- }
- fprintf(out,
- "\n // Adding uid field for atom "
- "(%d)%s\n",
- (*atomIt)->code, (*atomIt)->name.c_str());
- fprintf(out, " uidField[%d /* %s */] = %d;\n", (*atomIt)->code,
- make_constant_name((*atomIt)->name).c_str(), (*atomIt)->uidField);
- }
-
- fprintf(out, " return uidField;\n");
- fprintf(out, "};\n");
-
- fprintf(out,
- "const std::map<int, int> AtomsInfo::kAtomsWithUidField = "
- "getAtomUidField();\n");
-
fprintf(out,
"static std::map<int, StateAtomFieldOptions> "
"getStateAtomFieldOptions() {\n");
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index eeb006ee6ab2..cf11f4347503 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -53,6 +53,9 @@ rule com.google.protobuf.** com.android.server.x.wifi.protobuf.@1
rule com.android.internal.messages.SystemMessageProto* com.android.server.x.wifi.messages.SystemMessageProto@1
# Use our statically linked PlatformProperties library
rule android.sysprop.** com.android.server.x.wifi.sysprop.@1
+# Use our statically linked HIDL stubs
+rule android.hardware.** com.android.server.x.wifi.hardware.@1
+rule android.hidl.** com.android.server.x.wifi.hidl.@1
# used by both framework-wifi and wifi-service
rule android.content.pm.BaseParceledListSlice* android.x.net.wifi.util.BaseParceledListSlice@1