summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PREUPLOAD.cfg1
-rw-r--r--StubLibraries.bp49
-rw-r--r--apct-tests/perftests/multiuser/AndroidTest.xml3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/JobSchedulerBackgroundThread.java13
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java404
-rw-r--r--api/current.txt17
-rwxr-xr-xapi/system-current.txt14
-rw-r--r--api/test-current.txt5
-rw-r--r--cmds/incidentd/src/IncidentService.cpp4
-rw-r--r--cmds/statsd/src/atoms.proto54
-rw-r--r--cmds/statsd/src/condition/CombinationConditionTracker.cpp29
-rw-r--r--cmds/statsd/src/condition/CombinationConditionTracker.h2
-rw-r--r--cmds/statsd/src/condition/ConditionTracker.h8
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.cpp9
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.h2
-rw-r--r--cmds/statsd/src/matchers/AtomMatchingTracker.h11
-rw-r--r--cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp17
-rw-r--r--cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h7
-rw-r--r--cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp8
-rw-r--r--cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h5
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp9
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp3
-rw-r--r--cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp30
-rw-r--r--cmds/uinput/Android.bp18
-rw-r--r--cmds/uinput/MODULE_LICENSE_APACHE20
-rw-r--r--cmds/uinput/NOTICE190
-rw-r--r--cmds/uinput/README.md166
-rw-r--r--cmds/uinput/jni/Android.bp23
-rw-r--r--cmds/uinput/jni/com_android_commands_uinput_Device.cpp351
-rw-r--r--cmds/uinput/jni/com_android_commands_uinput_Device.h67
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Device.java232
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Event.java454
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/InputAbsInfo.aidl26
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Uinput.java140
-rwxr-xr-xcmds/uinput/uinput9
-rw-r--r--core/java/android/app/AppOpsManager.java46
-rw-r--r--core/java/android/app/ContextImpl.java13
-rw-r--r--core/java/android/app/IActivityManager.aidl3
-rw-r--r--core/java/android/app/ResourcesManager.java4
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java46
-rw-r--r--core/java/android/content/pm/PackageInstaller.java8
-rw-r--r--core/java/android/os/BatteryStats.java2
-rw-r--r--core/java/android/os/GraphicsEnvironment.java9
-rw-r--r--core/java/android/os/IPowerManager.aidl4
-rw-r--r--core/java/android/os/ParcelDuration.aidl (renamed from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManager.java)29
-rw-r--r--core/java/android/os/ParcelDuration.java103
-rw-r--r--core/java/android/os/PowerManager.java77
-rw-r--r--core/java/android/os/Trace.java2
-rw-r--r--core/java/android/provider/DeviceConfig.java7
-rw-r--r--core/java/android/provider/Settings.java44
-rw-r--r--core/java/android/service/autofill/AutofillService.java14
-rw-r--r--core/java/android/view/InputWindowHandle.java7
-rw-r--r--core/java/android/view/InsetsController.java11
-rw-r--r--core/java/android/view/InsetsState.java2
-rw-r--r--core/java/android/view/ViewPropertyAnimator.java3
-rw-r--r--core/java/android/view/ViewRootImpl.java3
-rw-r--r--core/java/android/webkit/PacProcessor.java36
-rw-r--r--core/java/android/webkit/WebViewFactoryProvider.java9
-rw-r--r--core/java/android/window/ITaskOrganizerController.aidl6
-rw-r--r--core/java/android/window/TaskOrganizer.java8
-rw-r--r--core/java/android/window/TaskOrganizerTaskEmbedder.java2
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java2
-rw-r--r--core/java/com/android/internal/util/Preconditions.java14
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl3
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java29
-rw-r--r--core/java/com/android/internal/widget/VerifyCredentialResponse.java37
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.cpp14
-rw-r--r--core/jni/android_os_HwRemoteBinder.cpp19
-rw-r--r--core/jni/android_os_HwRemoteBinder.h13
-rw-r--r--core/proto/android/providers/settings/global.proto2
-rw-r--r--core/proto/android/server/powermanagerservice.proto10
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java14
-rw-r--r--data/etc/car/Android.bp7
-rw-r--r--data/etc/car/com.android.car.floatingcardslauncher.xml25
-rw-r--r--graphics/java/android/graphics/BlurShader.java65
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java161
-rw-r--r--libs/WindowManager/Shell/tests/src/com/android/wm/shell/ShellTaskOrganizerTests.java121
-rw-r--r--libs/androidfw/ResourceTypes.cpp1
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/jni/Shader.cpp23
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp4
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp4
-rw-r--r--libs/hwui/shader/BlurShader.cpp38
-rw-r--r--libs/hwui/shader/BlurShader.h42
-rw-r--r--libs/hwui/shader/Shader.h4
-rw-r--r--location/java/android/location/timezone/LocationTimeZoneEvent.java54
-rw-r--r--location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java2
-rw-r--r--location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java18
-rw-r--r--non-updatable-api/current.txt17
-rw-r--r--non-updatable-api/system-current.txt14
-rw-r--r--packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml3
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java2
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java18
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml10
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/proguard.flags3
-rw-r--r--packages/SystemUI/res-keyguard/drawable/face_auth_wallpaper.pngbin0 -> 709067 bytes
-rw-r--r--packages/SystemUI/res/layout/bubble_stack_user_education.xml15
-rw-r--r--packages/SystemUI/res/layout/bubbles_manage_button_education.xml124
-rw-r--r--packages/SystemUI/res/layout/notification_conversation_info.xml2
-rw-r--r--packages/SystemUI/res/layout/partial_conversation_info.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/ExpandHelper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java283
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt98
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt134
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeService.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt193
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java (renamed from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt (renamed from packages/SystemUI/src/com/android/systemui/onehanded/dagger/OneHandedModule.java)25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSection.java)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java355
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java165
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java30
-rw-r--r--packages/SystemUI/tests/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/tests/res/values/strings.xml21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt135
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java)16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java101
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java128
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java105
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java183
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java100
-rw-r--r--services/Android.bp4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java18
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java50
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java52
-rw-r--r--services/core/java/com/android/server/Watchdog.java1
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java11
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java13
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java36
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java2
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java8
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java7
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java6
-rwxr-xr-xservices/core/java/com/android/server/audio/AudioService.java150
-rw-r--r--services/core/java/com/android/server/connectivity/PermissionMonitor.java260
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java17
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java104
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java13
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java100
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java16
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java14
-rw-r--r--services/core/java/com/android/server/pm/Settings.java109
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java2
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java110
-rw-r--r--services/core/java/com/android/server/pm/permission/BasePermission.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java362
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java47
-rw-r--r--services/core/java/com/android/server/power/Notifier.java15
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java154
-rw-r--r--services/core/java/com/android/server/storage/StorageSessionController.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java11
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java28
-rw-r--r--services/core/java/com/android/server/wm/DragState.java5
-rw-r--r--services/core/java/com/android/server/wm/InputConsumerImpl.java3
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java19
-rw-r--r--services/core/java/com/android/server/wm/InsetsControlTarget.java8
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java10
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java48
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java6
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java120
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java4
-rw-r--r--services/core/java/com/android/server/wm/UnknownAppVisibilityController.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java16
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java3
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java948
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java324
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java121
-rw-r--r--services/tests/servicestests/src/android/location/timezone/LocationTimeZoneEventTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java63
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java96
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java5
-rw-r--r--startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java2
-rw-r--r--telephony/api/system-current.txt7
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java2
-rw-r--r--telephony/java/android/telephony/CellIdentityNr.java12
-rw-r--r--telephony/java/android/telephony/CellInfoNr.java14
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java75
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl4
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml9
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java110
-rw-r--r--tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java2
-rw-r--r--tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java2
-rw-r--r--tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java391
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java56
269 files changed, 8064 insertions, 3316 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 2fd2e33bbc37..fc5efc6e03ac 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,6 +6,7 @@ clang_format = true
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
cmds/hid/
cmds/input/
+ cmds/uinput/
core/jni/
libs/input/
services/core/jni/
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 6b2987ddefa0..bb6538739c49 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -312,6 +312,15 @@ java_defaults {
compile_dex: true,
}
+java_defaults {
+ name: "android_stubs_dists_default",
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android.jar",
+ },
+}
+
java_library_static {
name: "android_monolith_stubs_current",
srcs: [ ":api-stubs-docs" ],
@@ -347,7 +356,21 @@ java_library_static {
name: "android_system_monolith_stubs_current",
srcs: [ ":system-api-stubs-docs" ],
static_libs: [ "private-stub-annotations-jar" ],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/system",
+ },
+ dists: [
+ {
+ // Legacy dist path
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android_system.jar",
+ },
+ ],
}
java_library_static {
@@ -379,14 +402,34 @@ java_library_static {
name: "android_test_stubs_current",
srcs: [ ":test-api-stubs-docs" ],
static_libs: [ "private-stub-annotations-jar" ],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/test",
+ },
+ dists: [
+ {
+ // Legacy dist path
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android_test.jar",
+ },
+ ],
}
java_library_static {
name: "android_module_lib_stubs_current",
srcs: [ ":module-lib-api-stubs-docs-non-updatable" ],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
libs: ["sdk_system_29_android"],
+ dist: {
+ dir: "apistubs/android/module-lib",
+ },
}
java_library_static {
diff --git a/apct-tests/perftests/multiuser/AndroidTest.xml b/apct-tests/perftests/multiuser/AndroidTest.xml
index c7929af6077f..fbe589248338 100644
--- a/apct-tests/perftests/multiuser/AndroidTest.xml
+++ b/apct-tests/perftests/multiuser/AndroidTest.xml
@@ -27,6 +27,9 @@
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config_detailed.textproto" value="/sdcard/sample.textproto" />
</target_preparer>
<!-- Needed for pulling the collected trace config on to the host -->
diff --git a/apex/jobscheduler/service/java/com/android/server/JobSchedulerBackgroundThread.java b/apex/jobscheduler/service/java/com/android/server/JobSchedulerBackgroundThread.java
index 91d254d16b09..a413f7b1f3ca 100644
--- a/apex/jobscheduler/service/java/com/android/server/JobSchedulerBackgroundThread.java
+++ b/apex/jobscheduler/service/java/com/android/server/JobSchedulerBackgroundThread.java
@@ -17,10 +17,13 @@
package com.android.server;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Trace;
+import java.util.concurrent.Executor;
+
/**
* Shared singleton background thread.
*
@@ -31,6 +34,7 @@ public final class JobSchedulerBackgroundThread extends HandlerThread {
private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
private static JobSchedulerBackgroundThread sInstance;
private static Handler sHandler;
+ private static Executor sHandlerExecutor;
private JobSchedulerBackgroundThread() {
super("jobscheduler.bg", android.os.Process.THREAD_PRIORITY_BACKGROUND);
@@ -45,6 +49,7 @@ public final class JobSchedulerBackgroundThread extends HandlerThread {
looper.setSlowLogThresholdMs(
SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
sHandler = new Handler(sInstance.getLooper());
+ sHandlerExecutor = new HandlerExecutor(sHandler);
}
}
@@ -63,4 +68,12 @@ public final class JobSchedulerBackgroundThread extends HandlerThread {
return sHandler;
}
}
+
+ /** Returns the singleton handler executor for JobSchedulerBackgroundThread */
+ public static Executor getExecutor() {
+ synchronized (JobSchedulerBackgroundThread.class) {
+ ensureThreadLocked();
+ return sHandlerExecutor;
+ }
+ }
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index a1a5004447a6..b35a7be1f689 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -166,7 +166,7 @@ class JobConcurrencyManager {
// Note: we can't directly do postDelayed(this::rampUpForScreenOn), because
// we need the exact same instance for removeCallbacks().
mHandler.postDelayed(mRampUpForScreenOff,
- mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.getValue());
+ mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
}
}
}
@@ -189,7 +189,7 @@ class JobConcurrencyManager {
}
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
if ((mLastScreenOffRealtime
- + mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.getValue())
+ + mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS)
> now) {
return;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 3234b274eaa7..cf4caea9c487 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -39,7 +39,6 @@ import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -50,7 +49,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
-import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
@@ -67,11 +65,10 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.os.WorkSource;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -88,6 +85,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.server.AppStateTracker;
import com.android.server.AppStateTrackerImpl;
import com.android.server.DeviceIdleInternal;
+import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
import com.android.server.SystemService.TargetUser;
import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
@@ -329,39 +327,70 @@ public class JobSchedulerService extends com.android.server.SystemService
// -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
- private class ConstantsObserver extends ContentObserver {
- private ContentResolver mResolver;
-
- public ConstantsObserver(Handler handler) {
- super(handler);
- }
-
- public void start(ContentResolver resolver) {
- mResolver = resolver;
- mResolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
- updateConstants();
+ private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener {
+ public void start() {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ JobSchedulerBackgroundThread.getExecutor(), this);
+ // Load all the constants.
+ onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
}
@Override
- public void onChange(boolean selfChange, Uri uri) {
- updateConstants();
- }
-
- private void updateConstants() {
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ boolean apiQuotaScheduleUpdated = false;
+ boolean concurrencyUpdated = false;
synchronized (mLock) {
- try {
- mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
- Settings.Global.JOB_SCHEDULER_CONSTANTS));
- for (int controller = 0; controller < mControllers.size(); controller++) {
- final StateController sc = mControllers.get(controller);
- sc.onConstantsUpdatedLocked();
+ for (String name : properties.getKeyset()) {
+ if (name == null) {
+ continue;
}
- updateQuotaTracker();
- } catch (IllegalArgumentException e) {
- // Failed to parse the settings string, log this and move on
- // with defaults.
- Slog.e(TAG, "Bad jobscheduler settings", e);
+ switch (name) {
+ case Constants.KEY_ENABLE_API_QUOTAS:
+ case Constants.KEY_API_QUOTA_SCHEDULE_COUNT:
+ case Constants.KEY_API_QUOTA_SCHEDULE_WINDOW_MS:
+ case Constants.KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT:
+ case Constants.KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION:
+ if (!apiQuotaScheduleUpdated) {
+ mConstants.updateApiQuotaConstantsLocked();
+ updateQuotaTracker();
+ apiQuotaScheduleUpdated = true;
+ }
+ break;
+ case Constants.KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT:
+ case Constants.KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS:
+ mConstants.updateBatchingConstantsLocked();
+ break;
+ case Constants.KEY_HEAVY_USE_FACTOR:
+ case Constants.KEY_MODERATE_USE_FACTOR:
+ mConstants.updateUseFactorConstantsLocked();
+ break;
+ case Constants.KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS:
+ if (!concurrencyUpdated) {
+ mConstants.updateConcurrencyConstantsLocked();
+ concurrencyUpdated = true;
+ }
+ break;
+ case Constants.KEY_MIN_LINEAR_BACKOFF_TIME_MS:
+ case Constants.KEY_MIN_EXP_BACKOFF_TIME_MS:
+ mConstants.updateBackoffConstantsLocked();
+ break;
+ case Constants.KEY_CONN_CONGESTION_DELAY_FRAC:
+ case Constants.KEY_CONN_PREFETCH_RELAX_FRAC:
+ mConstants.updateConnectivityConstantsLocked();
+ break;
+ default:
+ // Too many max_job_* strings to list.
+ if (name.startsWith(Constants.KEY_PREFIX_MAX_JOB)
+ && !concurrencyUpdated) {
+ mConstants.updateConcurrencyConstantsLocked();
+ concurrencyUpdated = true;
+ }
+ break;
+ }
+ }
+ for (int controller = 0; controller < mControllers.size(); controller++) {
+ final StateController sc = mControllers.get(controller);
+ sc.onConstantsUpdatedLocked();
}
}
}
@@ -376,53 +405,52 @@ public class JobSchedulerService extends com.android.server.SystemService
}
static class MaxJobCounts {
- private final KeyValueListParser.IntValue mTotal;
- private final KeyValueListParser.IntValue mMaxBg;
- private final KeyValueListParser.IntValue mMinBg;
+ private final int mTotalDefault;
+ private final String mTotalKey;
+ private final int mMaxBgDefault;
+ private final String mMaxBgKey;
+ private final int mMinBgDefault;
+ private final String mMinBgKey;
+ private int mTotal;
+ private int mMaxBg;
+ private int mMinBg;
MaxJobCounts(int totalDefault, String totalKey,
int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) {
- mTotal = new KeyValueListParser.IntValue(totalKey, totalDefault);
- mMaxBg = new KeyValueListParser.IntValue(maxBgKey, maxBgDefault);
- mMinBg = new KeyValueListParser.IntValue(minBgKey, minBgDefault);
+ mTotalKey = totalKey;
+ mTotal = mTotalDefault = totalDefault;
+ mMaxBgKey = maxBgKey;
+ mMaxBg = mMaxBgDefault = maxBgDefault;
+ mMinBgKey = minBgKey;
+ mMinBg = mMinBgDefault = minBgDefault;
}
- public void parse(KeyValueListParser parser) {
- mTotal.parse(parser);
- mMaxBg.parse(parser);
- mMinBg.parse(parser);
+ public void update() {
+ mTotal = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ mTotalKey, mTotalDefault);
+ mMaxBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ mMaxBgKey, mMaxBgDefault);
+ mMinBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ mMinBgKey, mMinBgDefault);
- if (mTotal.getValue() < 1) {
- mTotal.setValue(1);
- } else if (mTotal.getValue() > MAX_JOB_CONTEXTS_COUNT) {
- mTotal.setValue(MAX_JOB_CONTEXTS_COUNT);
- }
+ // Ensure total in the range [1, MAX_JOB_CONTEXTS_COUNT].
+ mTotal = Math.min(Math.max(1, mTotal), MAX_JOB_CONTEXTS_COUNT);
- if (mMaxBg.getValue() < 1) {
- mMaxBg.setValue(1);
- } else if (mMaxBg.getValue() > mTotal.getValue()) {
- mMaxBg.setValue(mTotal.getValue());
- }
- if (mMinBg.getValue() < 0) {
- mMinBg.setValue(0);
- } else {
- if (mMinBg.getValue() > mMaxBg.getValue()) {
- mMinBg.setValue(mMaxBg.getValue());
- }
- if (mMinBg.getValue() >= mTotal.getValue()) {
- mMinBg.setValue(mTotal.getValue() - 1);
- }
- }
+ // Ensure maxBg in the range [1, total].
+ mMaxBg = Math.min(Math.max(1, mMaxBg), mTotal);
+
+ // Ensure minBg in the range [0, min(maxBg, total - 1)]
+ mMinBg = Math.min(Math.max(0, mMinBg), Math.min(mMaxBg, mTotal - 1));
}
/** Total number of jobs to run simultaneously. */
public int getMaxTotal() {
- return mTotal.getValue();
+ return mTotal;
}
/** Max number of BG (== owned by non-TOP apps) jobs to run simultaneously. */
public int getMaxBg() {
- return mMaxBg.getValue();
+ return mMaxBg;
}
/**
@@ -430,20 +458,34 @@ public class JobSchedulerService extends com.android.server.SystemService
* pending, rather than always running the TOTAL number of FG jobs.
*/
public int getMinBg() {
- return mMinBg.getValue();
+ return mMinBg;
}
public void dump(PrintWriter pw, String prefix) {
- mTotal.dump(pw, prefix);
- mMaxBg.dump(pw, prefix);
- mMinBg.dump(pw, prefix);
+ pw.print(prefix);
+ pw.print(mTotalKey);
+ pw.print("=");
+ pw.print(mTotal);
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(mMaxBgKey);
+ pw.print("=");
+ pw.print(mMaxBg);
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(mMinBgKey);
+ pw.print("=");
+ pw.print(mMinBg);
+ pw.println();
}
public void dumpProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- mTotal.dumpProto(proto, MaxJobCountsProto.TOTAL_JOBS);
- mMaxBg.dumpProto(proto, MaxJobCountsProto.MAX_BG);
- mMinBg.dumpProto(proto, MaxJobCountsProto.MIN_BG);
+ proto.write(MaxJobCountsProto.TOTAL_JOBS, mTotal);
+ proto.write(MaxJobCountsProto.MAX_BG, mMaxBg);
+ proto.write(MaxJobCountsProto.MIN_BG, mMinBg);
proto.end(token);
}
}
@@ -476,23 +518,11 @@ public class JobSchedulerService extends com.android.server.SystemService
}
/**
- * All times are in milliseconds. These constants are kept synchronized with the system
- * global Settings. Any access to this class or its fields should be done while
+ * All times are in milliseconds. Any access to this class or its fields should be done while
* holding the JobSchedulerService.mLock lock.
*/
public static class Constants {
// Key names stored in the settings value.
- // TODO(124466289): remove deprecated flags when we migrate to DeviceConfig
- private static final String DEPRECATED_KEY_MIN_IDLE_COUNT = "min_idle_count";
- private static final String DEPRECATED_KEY_MIN_CHARGING_COUNT = "min_charging_count";
- private static final String DEPRECATED_KEY_MIN_BATTERY_NOT_LOW_COUNT =
- "min_battery_not_low_count";
- private static final String DEPRECATED_KEY_MIN_STORAGE_NOT_LOW_COUNT =
- "min_storage_not_low_count";
- private static final String DEPRECATED_KEY_MIN_CONNECTIVITY_COUNT =
- "min_connectivity_count";
- private static final String DEPRECATED_KEY_MIN_CONTENT_COUNT = "min_content_count";
- private static final String DEPRECATED_KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
private static final String KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT =
"min_ready_non_active_jobs_count";
private static final String KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS =
@@ -500,28 +530,10 @@ public class JobSchedulerService extends com.android.server.SystemService
private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
- // The following values used to be used on P and below. Do not reuse them.
- private static final String DEPRECATED_KEY_FG_JOB_COUNT = "fg_job_count";
- private static final String DEPRECATED_KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
- private static final String DEPRECATED_KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
- private static final String DEPRECATED_KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
- private static final String DEPRECATED_KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
-
- private static final String DEPRECATED_KEY_MAX_STANDARD_RESCHEDULE_COUNT
- = "max_standard_reschedule_count";
- private static final String DEPRECATED_KEY_MAX_WORK_RESCHEDULE_COUNT =
- "max_work_reschedule_count";
- private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
- private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
- private static final String DEPRECATED_KEY_STANDBY_HEARTBEAT_TIME =
- "standby_heartbeat_time";
- private static final String DEPRECATED_KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
- private static final String DEPRECATED_KEY_STANDBY_FREQUENT_BEATS =
- "standby_frequent_beats";
- private static final String DEPRECATED_KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
+ private static final String KEY_MIN_LINEAR_BACKOFF_TIME_MS = "min_linear_backoff_time_ms";
+ private static final String KEY_MIN_EXP_BACKOFF_TIME_MS = "min_exp_backoff_time_ms";
private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
- private static final String DEPRECATED_KEY_USE_HEARTBEATS = "use_heartbeats";
private static final String KEY_ENABLE_API_QUOTAS = "enable_api_quotas";
private static final String KEY_API_QUOTA_SCHEDULE_COUNT = "aq_schedule_count";
private static final String KEY_API_QUOTA_SCHEDULE_WINDOW_MS = "aq_schedule_window_ms";
@@ -530,12 +542,15 @@ public class JobSchedulerService extends com.android.server.SystemService
private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
"aq_schedule_return_failure";
+ private static final String KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
+ "screen_off_job_concurrency_increase_delay_ms";
+
private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
- private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
- private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
+ private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
+ private static final long DEFAULT_MIN_EXP_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
private static final boolean DEFAULT_ENABLE_API_QUOTAS = true;
@@ -543,6 +558,7 @@ public class JobSchedulerService extends com.android.server.SystemService
private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS;
private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true;
private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false;
+ private static final long DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = 30_000;
/**
* Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
@@ -564,59 +580,61 @@ public class JobSchedulerService extends com.android.server.SystemService
*/
float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
+ /** Prefix for all of the max_job constants. */
+ private static final String KEY_PREFIX_MAX_JOB = "max_job_";
+
// Max job counts for screen on / off, for each memory trim level.
final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON =
new MaxJobCountsPerMemoryTrimLevel(
new MaxJobCounts(
- 8, "max_job_total_on_normal",
- 6, "max_job_max_bg_on_normal",
- 2, "max_job_min_bg_on_normal"),
+ 8, KEY_PREFIX_MAX_JOB + "total_on_normal",
+ 6, KEY_PREFIX_MAX_JOB + "max_bg_on_normal",
+ 2, KEY_PREFIX_MAX_JOB + "min_bg_on_normal"),
new MaxJobCounts(
- 8, "max_job_total_on_moderate",
- 4, "max_job_max_bg_on_moderate",
- 2, "max_job_min_bg_on_moderate"),
+ 8, KEY_PREFIX_MAX_JOB + "total_on_moderate",
+ 4, KEY_PREFIX_MAX_JOB + "max_bg_on_moderate",
+ 2, KEY_PREFIX_MAX_JOB + "min_bg_on_moderate"),
new MaxJobCounts(
- 5, "max_job_total_on_low",
- 1, "max_job_max_bg_on_low",
- 1, "max_job_min_bg_on_low"),
+ 5, KEY_PREFIX_MAX_JOB + "total_on_low",
+ 1, KEY_PREFIX_MAX_JOB + "max_bg_on_low",
+ 1, KEY_PREFIX_MAX_JOB + "min_bg_on_low"),
new MaxJobCounts(
- 5, "max_job_total_on_critical",
- 1, "max_job_max_bg_on_critical",
- 1, "max_job_min_bg_on_critical"));
+ 5, KEY_PREFIX_MAX_JOB + "total_on_critical",
+ 1, KEY_PREFIX_MAX_JOB + "max_bg_on_critical",
+ 1, KEY_PREFIX_MAX_JOB + "min_bg_on_critical"));
final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_OFF =
new MaxJobCountsPerMemoryTrimLevel(
new MaxJobCounts(
- 10, "max_job_total_off_normal",
- 6, "max_job_max_bg_off_normal",
- 2, "max_job_min_bg_off_normal"),
+ 10, KEY_PREFIX_MAX_JOB + "total_off_normal",
+ 6, KEY_PREFIX_MAX_JOB + "max_bg_off_normal",
+ 2, KEY_PREFIX_MAX_JOB + "min_bg_off_normal"),
new MaxJobCounts(
- 10, "max_job_total_off_moderate",
- 4, "max_job_max_bg_off_moderate",
- 2, "max_job_min_bg_off_moderate"),
+ 10, KEY_PREFIX_MAX_JOB + "total_off_moderate",
+ 4, KEY_PREFIX_MAX_JOB + "max_bg_off_moderate",
+ 2, KEY_PREFIX_MAX_JOB + "min_bg_off_moderate"),
new MaxJobCounts(
- 5, "max_job_total_off_low",
- 1, "max_job_max_bg_off_low",
- 1, "max_job_min_bg_off_low"),
+ 5, KEY_PREFIX_MAX_JOB + "total_off_low",
+ 1, KEY_PREFIX_MAX_JOB + "max_bg_off_low",
+ 1, KEY_PREFIX_MAX_JOB + "min_bg_off_low"),
new MaxJobCounts(
- 5, "max_job_total_off_critical",
- 1, "max_job_max_bg_off_critical",
- 1, "max_job_min_bg_off_critical"));
+ 5, KEY_PREFIX_MAX_JOB + "total_off_critical",
+ 1, KEY_PREFIX_MAX_JOB + "max_bg_off_critical",
+ 1, KEY_PREFIX_MAX_JOB + "min_bg_off_critical"));
/** Wait for this long after screen off before increasing the job concurrency. */
- final KeyValueListParser.IntValue SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
- new KeyValueListParser.IntValue(
- "screen_off_job_concurrency_increase_delay_ms", 30_000);
+ long SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
+ DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS;
/**
* The minimum backoff time to allow for linear backoff.
*/
- long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
+ long MIN_LINEAR_BACKOFF_TIME_MS = DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS;
/**
* The minimum backoff time to allow for exponential backoff.
*/
- long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
+ long MIN_EXP_BACKOFF_TIME_MS = DEFAULT_MIN_EXP_BACKOFF_TIME_MS;
/**
* The fraction of a job's running window that must pass before we
@@ -652,61 +670,78 @@ public class JobSchedulerService extends com.android.server.SystemService
public boolean API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
-
- void updateConstantsLocked(String value) {
- try {
- mParser.setString(value);
- } catch (Exception e) {
- // Failed to parse the settings string, log this and move on
- // with defaults.
- Slog.e(TAG, "Bad jobscheduler settings", e);
- }
-
- MIN_READY_NON_ACTIVE_JOBS_COUNT = mParser.getInt(
+ private void updateBatchingConstantsLocked() {
+ MIN_READY_NON_ACTIVE_JOBS_COUNT = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT,
DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT);
- MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = mParser.getLong(
+ MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS);
- HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
+ }
+
+ private void updateUseFactorConstantsLocked() {
+ HEAVY_USE_FACTOR = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_HEAVY_USE_FACTOR,
DEFAULT_HEAVY_USE_FACTOR);
- MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
+ MODERATE_USE_FACTOR = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_MODERATE_USE_FACTOR,
DEFAULT_MODERATE_USE_FACTOR);
+ }
- MAX_JOB_COUNTS_SCREEN_ON.normal.parse(mParser);
- MAX_JOB_COUNTS_SCREEN_ON.moderate.parse(mParser);
- MAX_JOB_COUNTS_SCREEN_ON.low.parse(mParser);
- MAX_JOB_COUNTS_SCREEN_ON.critical.parse(mParser);
+ void updateConcurrencyConstantsLocked() {
+ MAX_JOB_COUNTS_SCREEN_ON.normal.update();
+ MAX_JOB_COUNTS_SCREEN_ON.moderate.update();
+ MAX_JOB_COUNTS_SCREEN_ON.low.update();
+ MAX_JOB_COUNTS_SCREEN_ON.critical.update();
- MAX_JOB_COUNTS_SCREEN_OFF.normal.parse(mParser);
- MAX_JOB_COUNTS_SCREEN_OFF.moderate.parse(mParser);
- MAX_JOB_COUNTS_SCREEN_OFF.low.parse(mParser);
- MAX_JOB_COUNTS_SCREEN_OFF.critical.parse(mParser);
+ MAX_JOB_COUNTS_SCREEN_OFF.normal.update();
+ MAX_JOB_COUNTS_SCREEN_OFF.moderate.update();
+ MAX_JOB_COUNTS_SCREEN_OFF.low.update();
+ MAX_JOB_COUNTS_SCREEN_OFF.critical.update();
- SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.parse(mParser);
+ SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS,
+ DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
+ }
- MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
- DEFAULT_MIN_LINEAR_BACKOFF_TIME);
- MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
- DEFAULT_MIN_EXP_BACKOFF_TIME);
- CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
+ private void updateBackoffConstantsLocked() {
+ MIN_LINEAR_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_MIN_LINEAR_BACKOFF_TIME_MS,
+ DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS);
+ MIN_EXP_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_MIN_EXP_BACKOFF_TIME_MS,
+ DEFAULT_MIN_EXP_BACKOFF_TIME_MS);
+ }
+
+ private void updateConnectivityConstantsLocked() {
+ CONN_CONGESTION_DELAY_FRAC = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_CONN_CONGESTION_DELAY_FRAC,
DEFAULT_CONN_CONGESTION_DELAY_FRAC);
- CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
+ CONN_PREFETCH_RELAX_FRAC = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_CONN_PREFETCH_RELAX_FRAC,
DEFAULT_CONN_PREFETCH_RELAX_FRAC);
+ }
- ENABLE_API_QUOTAS = mParser.getBoolean(KEY_ENABLE_API_QUOTAS,
- DEFAULT_ENABLE_API_QUOTAS);
+ private void updateApiQuotaConstantsLocked() {
+ ENABLE_API_QUOTAS = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_ENABLE_API_QUOTAS, DEFAULT_ENABLE_API_QUOTAS);
// Set a minimum value on the quota limit so it's not so low that it interferes with
// legitimate use cases.
API_QUOTA_SCHEDULE_COUNT = Math.max(250,
- mParser.getInt(KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT));
- API_QUOTA_SCHEDULE_WINDOW_MS = mParser.getDurationMillis(
+ DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT));
+ API_QUOTA_SCHEDULE_WINDOW_MS = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
- API_QUOTA_SCHEDULE_THROW_EXCEPTION = mParser.getBoolean(
+ API_QUOTA_SCHEDULE_THROW_EXCEPTION = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION);
- API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = mParser.getBoolean(
+ API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT);
}
@@ -731,10 +766,11 @@ public class JobSchedulerService extends com.android.server.SystemService
MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, "");
MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, "");
- SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dump(pw, "");
+ pw.print(KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS,
+ SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS).println();
- pw.print(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
- pw.print(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
+ pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println();
+ pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println();
pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
pw.print(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
@@ -760,11 +796,11 @@ public class JobSchedulerService extends com.android.server.SystemService
MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON);
MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF);
- SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dumpProto(proto,
- ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
+ proto.write(ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS,
+ SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
- proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
- proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
+ proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS);
+ proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS);
proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
@@ -1407,7 +1443,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mHandler = new JobHandler(context.getMainLooper());
mConstants = new Constants();
- mConstantsObserver = new ConstantsObserver(mHandler);
+ mConstantsObserver = new ConstantsObserver();
mJobSchedulerStub = new JobSchedulerStub();
mConcurrencyManager = new JobConcurrencyManager(this);
@@ -1521,7 +1557,7 @@ public class JobSchedulerService extends com.android.server.SystemService
@Override
public void onBootPhase(int phase) {
if (PHASE_SYSTEM_SERVICES_READY == phase) {
- mConstantsObserver.start(getContext().getContentResolver());
+ mConstantsObserver.start();
for (StateController controller : mControllers) {
controller.onSystemServicesReady();
}
@@ -1693,8 +1729,8 @@ public class JobSchedulerService extends com.android.server.SystemService
switch (job.getBackoffPolicy()) {
case JobInfo.BACKOFF_POLICY_LINEAR: {
long backoff = initialBackoffMillis;
- if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
- backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
+ if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME_MS) {
+ backoff = mConstants.MIN_LINEAR_BACKOFF_TIME_MS;
}
delayMillis = backoff * backoffAttempts;
} break;
@@ -1704,8 +1740,8 @@ public class JobSchedulerService extends com.android.server.SystemService
}
case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
long backoff = initialBackoffMillis;
- if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
- backoff = mConstants.MIN_EXP_BACKOFF_TIME;
+ if (backoff < mConstants.MIN_EXP_BACKOFF_TIME_MS) {
+ backoff = mConstants.MIN_EXP_BACKOFF_TIME_MS;
}
delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
} break;
diff --git a/api/current.txt b/api/current.txt
index 9e0d88ecdfd1..36c67f275768 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11901,8 +11901,8 @@ package android.content.pm {
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
field public static final int INVALID_ID = -1; // 0xffffffff
field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
+ field public static final int STAGED_SESSION_CONFLICT = 4; // 0x4
field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
- field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
}
@@ -14250,6 +14250,10 @@ package android.graphics {
enum_constant public static final android.graphics.BlurMaskFilter.Blur SOLID;
}
+ public final class BlurShader extends android.graphics.Shader {
+ ctor public BlurShader(float, float, @Nullable android.graphics.Shader);
+ }
+
public class Camera {
ctor public Camera();
method public void applyToCanvas(android.graphics.Canvas);
@@ -36733,9 +36737,11 @@ package android.os {
public final class PowerManager {
method public void addThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
method public void addThermalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.PowerManager.OnThermalStatusChangedListener);
+ method @Nullable public java.time.Duration getBatteryDischargePrediction();
method public int getCurrentThermalStatus();
method public int getLocationPowerSaveMode();
method public float getThermalHeadroom(@IntRange(from=0, to=60) int);
+ method public boolean isBatteryDischargePredictionPersonalized();
method public boolean isDeviceIdleMode();
method public boolean isIgnoringBatteryOptimizations(String);
method public boolean isInteractive();
@@ -48352,7 +48358,9 @@ package android.telephony {
method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
method public boolean isConcurrentVoiceAndDataSupported();
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
method public boolean isEmergencyNumber(@NonNull String);
method public boolean isHearingAidCompatibilitySupported();
@@ -48374,6 +48382,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledForReason(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>);
method public boolean setLine1NumberForDisplay(String, String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNetworkSelectionModeAutomatic();
@@ -48421,6 +48430,10 @@ package android.telephony {
field public static final int DATA_CONNECTING = 1; // 0x1
field public static final int DATA_DISCONNECTED = 0; // 0x0
field public static final int DATA_DISCONNECTING = 4; // 0x4
+ field public static final int DATA_ENABLED_REASON_CARRIER = 2; // 0x2
+ field public static final int DATA_ENABLED_REASON_POLICY = 1; // 0x1
+ field public static final int DATA_ENABLED_REASON_THERMAL = 3; // 0x3
+ field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
field public static final int DATA_UNKNOWN = -1; // 0xffffffff
field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
@@ -55185,7 +55198,7 @@ package android.view {
}
public class ViewPropertyAnimator {
- method public android.view.ViewPropertyAnimator alpha(float);
+ method public android.view.ViewPropertyAnimator alpha(@FloatRange(from=0.0f, to=1.0f) float);
method public android.view.ViewPropertyAnimator alphaBy(float);
method public void cancel();
method public long getDuration();
diff --git a/api/system-current.txt b/api/system-current.txt
index 3ab164554da6..28c1eafca815 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8394,6 +8394,7 @@ package android.os {
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void suppressAmbientDisplay(@NonNull String, boolean);
@@ -11233,10 +11234,8 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
method public boolean isDataConnectivityPossible();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledWithReason(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
@@ -11268,7 +11267,6 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledWithReason(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
@@ -11306,10 +11304,6 @@ package android.telephony {
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
- field public static final int DATA_ENABLED_REASON_CARRIER = 2; // 0x2
- field public static final int DATA_ENABLED_REASON_POLICY = 1; // 0x1
- field public static final int DATA_ENABLED_REASON_THERMAL = 3; // 0x3
- field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
@@ -12744,8 +12738,8 @@ package android.webkit {
public interface PacProcessor {
method @Nullable public String findProxyForUrl(@NonNull String);
method @NonNull public static android.webkit.PacProcessor getInstance();
- method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(long);
- method public default long getNetworkHandle();
+ method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(@Nullable android.net.Network);
+ method @Nullable public default android.net.Network getNetwork();
method public default void releasePacProcessor();
method public boolean setProxyScript(@NonNull String);
}
@@ -12886,7 +12880,7 @@ package android.webkit {
method public android.webkit.CookieManager getCookieManager();
method public android.webkit.GeolocationPermissions getGeolocationPermissions();
method @NonNull public default android.webkit.PacProcessor getPacProcessor();
- method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(long);
+ method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(@Nullable android.net.Network);
method public android.webkit.ServiceWorkerController getServiceWorkerController();
method public android.webkit.WebViewFactoryProvider.Statics getStatics();
method @Deprecated public android.webkit.TokenBindingService getTokenBindingService();
diff --git a/api/test-current.txt b/api/test-current.txt
index 342bd81ac69b..a97a9e88a368 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2799,8 +2799,10 @@ package android.os {
public final class PowerManager {
method @RequiresPermission("android.permission.POWER_SAVER") public int getPowerSaveModeTrigger();
+ method @RequiresPermission("android.permission.DEVICE_POWER") public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
method @RequiresPermission("android.permission.POWER_SAVER") public boolean setDynamicPowerSaveHint(boolean, int);
method @RequiresPermission(anyOf={"android.permission.DEVICE_POWER", "android.permission.POWER_SAVER"}) public boolean setPowerSaveModeEnabled(boolean);
+ field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
field public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1; // 0x1
field public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0; // 0x0
}
@@ -3238,6 +3240,7 @@ package android.provider {
field public static final String NAMESPACE_AUTOFILL = "autofill";
field public static final String NAMESPACE_BIOMETRICS = "biometrics";
field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
+ field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
field public static final String NAMESPACE_PERMISSIONS = "permissions";
field public static final String NAMESPACE_PRIVACY = "privacy";
field public static final String NAMESPACE_ROLLBACK = "rollback";
@@ -5574,7 +5577,7 @@ package android.window {
method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer();
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static void setLaunchRoot(int, @NonNull android.window.WindowContainerToken);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void unregisterOrganizer();
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index dc1612575f38..13bf197aa9dc 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -554,6 +554,10 @@ status_t IncidentService::command(FILE* in, FILE* out, FILE* err, Vector<String8
return NO_ERROR;
}
if (!args[0].compare(String8("section"))) {
+ if (argCount == 1) {
+ fprintf(out, "Not enough arguments for section\n");
+ return NO_ERROR;
+ }
int id = atoi(args[1]);
int idx = 0;
while (SECTION_LIST[idx] != NULL) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 6b335ee1b923..e6e22bac05d4 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -485,6 +485,9 @@ message Atom {
NetworkTetheringReported network_tethering_reported =
303 [(module) = "network_tethering"];
ImeTouchReported ime_touch_reported = 304 [(module) = "sysui"];
+ UIInteractionFrameInfoReported ui_interaction_frame_info_reported =
+ 305 [(module) = "framework"];
+ UIActionLatencyReported ui_action_latency_reported = 306 [(module) = "framework"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -4004,7 +4007,7 @@ message Notification {
optional bool is_group_summary = 5;
// The section of the shade that the notification is in.
- // See NotificationSectionsManager.PriorityBucket.
+ // See SystemUI Notifications.proto.
enum NotificationSection {
SECTION_UNKNOWN = 0;
SECTION_HEADS_UP = 1;
@@ -4012,6 +4015,7 @@ message Notification {
SECTION_PEOPLE = 3;
SECTION_ALERTING = 4;
SECTION_SILENT = 5;
+ SECTION_FOREGROUND_SERVICE = 6;
}
optional NotificationSection section = 6;
}
@@ -5054,6 +5058,54 @@ message BlobOpened{
optional Result result = 4;
}
+/**
+ * Event to track Jank for various system interactions.
+ *
+ * Logged from:
+ * frameworks/base/core/java/android/os/aot/FrameTracker.java
+ */
+message UIInteractionFrameInfoReported {
+ enum InteractionType {
+ UNKNOWN = 0;
+ NOTIFICATION_SHADE_SWIPE = 1;
+ }
+
+ optional InteractionType interaction_type = 1;
+
+ // Number of frames rendered during the interaction.
+ optional int64 total_frames = 2;
+
+ // Number of frames that were skipped in rendering during the interaction.
+ optional int64 missed_frames = 3;
+
+ // Maximum time it took to render a single frame during the interaction.
+ optional int64 max_frame_time_nanos = 4;
+}
+
+/**
+ * Event to track various latencies in SystemUI.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/util/LatencyTracker.java
+ */
+message UIActionLatencyReported {
+ enum ActionType {
+ UNKNOWN = 0;
+ ACTION_EXPAND_PANEL = 1;
+ ACTION_TOGGLE_RECENTS = 2;
+ ACTION_FINGERPRINT_WAKE_AND_UNLOCK = 3;
+ ACTION_CHECK_CREDENTIAL = 4;
+ ACTION_CHECK_CREDENTIAL_UNLOCKED = 5;
+ ACTION_TURN_ON_SCREEN = 6;
+ ACTION_ROTATE_SCREEN = 7;
+ ACTION_FACE_WAKE_AND_UNLOCK = 8;
+ }
+
+ optional ActionType action = 1;
+
+ optional int64 latency_millis = 2;
+}
+
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index 7a1ece94fd3e..3b65f8225ee9 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -38,9 +38,23 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf
const vector<sp<ConditionTracker>>& allConditionTrackers,
const unordered_map<int64_t, int>& conditionIdIndexMap,
vector<bool>& stack,
- vector<ConditionState>& initialConditionCache) {
+ vector<ConditionState>& conditionCache) {
VLOG("Combination predicate init() %lld", (long long)mConditionId);
if (mInitialized) {
+ // All the children are guaranteed to be initialized, but the recursion is needed to
+ // fill the conditionCache properly, since another combination condition or metric
+ // might rely on this. The recursion is needed to compute the current condition.
+
+ // Init is called instead of isConditionMet so that the ConditionKey can be filled with the
+ // default key for sliced conditions, since we do not know all indirect descendants here.
+ for (const int childIndex : mChildren) {
+ if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
+ allConditionTrackers[childIndex]->init(allConditionConfig, allConditionTrackers,
+ conditionIdIndexMap, stack, conditionCache);
+ }
+ }
+ conditionCache[mIndex] =
+ evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
return true;
}
@@ -74,9 +88,8 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf
return false;
}
- bool initChildSucceeded =
- childTracker->init(allConditionConfig, allConditionTrackers, conditionIdIndexMap,
- stack, initialConditionCache);
+ bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
+ conditionIdIndexMap, stack, conditionCache);
if (!initChildSucceeded) {
ALOGW("Child initialization failed %lld ", (long long)child);
@@ -96,10 +109,10 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf
childTracker->getAtomMatchingTrackerIndex().end());
}
- mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation,
- initialConditionCache);
- initialConditionCache[mIndex] =
- evaluateCombinationCondition(mChildren, mLogicalOperation, initialConditionCache);
+ mUnSlicedPartCondition =
+ evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, conditionCache);
+ conditionCache[mIndex] =
+ evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
// unmark this node in the recursion stack.
stack[mIndex] = false;
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index 39ff0ab03266..a7fac3deaabe 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -33,7 +33,7 @@ public:
bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
- std::vector<ConditionState>& initialConditionCache) override;
+ std::vector<ConditionState>& conditionCache) override;
void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 9da1af427e5f..4e1253506be7 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -46,17 +46,19 @@ public:
// Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
// be done in the constructor, but we do it separately because (1) easy to return a bool to
// indicate whether the initialization is successful. (2) makes unit test easier.
+ // This function can also be called on config updates, in which case it does nothing other than
+ // fill the condition cache with the current condition.
// allConditionConfig: the list of all Predicate config from statsd_config.
// allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
// need to call init() on children conditions)
// conditionIdIndexMap: the mapping from condition id to its index.
// stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
- // initialConditionCache: tracks initial conditions of all ConditionTrackers.
+ // conditionCache: tracks initial conditions of all ConditionTrackers. returns the
+ // current condition if called on a config update.
virtual bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& conditionIdIndexMap,
- std::vector<bool>& stack,
- std::vector<ConditionState>& initialConditionCache) = 0;
+ std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) = 0;
// evaluate current condition given the new event.
// event: the new log event
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index efb4d4989425..f45759b6a77e 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -95,11 +95,14 @@ SimpleConditionTracker::~SimpleConditionTracker() {
bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
const vector<sp<ConditionTracker>>& allConditionTrackers,
const unordered_map<int64_t, int>& conditionIdIndexMap,
- vector<bool>& stack,
- vector<ConditionState>& initialConditionCache) {
+ vector<bool>& stack, vector<ConditionState>& conditionCache) {
// SimpleConditionTracker does not have dependency on other conditions, thus we just return
// if the initialization was successful.
- initialConditionCache[mIndex] = mInitialValue;
+ ConditionKey conditionKey;
+ if (mSliced) {
+ conditionKey[mConditionId] = DEFAULT_DIMENSION_KEY;
+ }
+ isConditionMet(conditionKey, allConditionTrackers, mSliced, conditionCache);
return mInitialized;
}
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index ea7f87bde2b8..1a9e35e38207 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -38,7 +38,7 @@ public:
bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
- std::vector<ConditionState>& initialConditionCache) override;
+ std::vector<ConditionState>& conditionCache) override;
void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
diff --git a/cmds/statsd/src/matchers/AtomMatchingTracker.h b/cmds/statsd/src/matchers/AtomMatchingTracker.h
index 5194f9970766..c1384972464c 100644
--- a/cmds/statsd/src/matchers/AtomMatchingTracker.h
+++ b/cmds/statsd/src/matchers/AtomMatchingTracker.h
@@ -52,6 +52,15 @@ public:
const std::unordered_map<int64_t, int>& matcherMap,
std::vector<bool>& stack) = 0;
+ // Update appropriate state on config updates. Primarily, all indices need to be updated.
+ // This matcher and all of its children are guaranteed to be preserved across the update.
+ // matcher: the AtomMatcher proto from the config.
+ // index: the index of this matcher in mAllAtomMatchingTrackers.
+ // atomMatchingTrackerMap: map from matcher id to index in mAllAtomMatchingTrackers
+ virtual bool onConfigUpdated(
+ const AtomMatcher& matcher, const int index,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) = 0;
+
// Called when a log event comes.
// event: the log event.
// allAtomMatchingTrackers: the list of all AtomMatchingTrackers. This is needed because the log
@@ -83,7 +92,7 @@ protected:
const int64_t mId;
// Index of this AtomMatchingTracker in MetricsManager's container.
- const int mIndex;
+ int mIndex;
// Whether this AtomMatchingTracker has been properly initialized.
bool mInitialized;
diff --git a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
index 19637a449c44..45685ce5bfee 100644
--- a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
@@ -93,6 +93,23 @@ bool CombinationAtomMatchingTracker::init(
return true;
}
+bool CombinationAtomMatchingTracker::onConfigUpdated(
+ const AtomMatcher& matcher, const int index,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+ mIndex = index;
+ mChildren.clear();
+ AtomMatcher_Combination combinationMatcher = matcher.combination();
+ for (const int64_t child : combinationMatcher.matcher()) {
+ const auto& pair = atomMatchingTrackerMap.find(child);
+ if (pair == atomMatchingTrackerMap.end()) {
+ ALOGW("Matcher %lld not found in the config", (long long)child);
+ return false;
+ }
+ mChildren.push_back(pair->second);
+ }
+ return true;
+}
+
void CombinationAtomMatchingTracker::onLogEvent(
const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
vector<MatchingState>& matcherResults) {
diff --git a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h
index 06a4932804f5..3160448b6c76 100644
--- a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h
+++ b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h
@@ -27,7 +27,7 @@ namespace os {
namespace statsd {
// Represents a AtomMatcher_Combination in the StatsdConfig.
-class CombinationAtomMatchingTracker : public virtual AtomMatchingTracker {
+class CombinationAtomMatchingTracker : public AtomMatchingTracker {
public:
CombinationAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash);
@@ -35,6 +35,9 @@ public:
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack);
+ bool onConfigUpdated(const AtomMatcher& matcher, const int index,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
+
~CombinationAtomMatchingTracker();
void onLogEvent(const LogEvent& event,
@@ -45,6 +48,8 @@ private:
LogicalOperation mLogicalOperation;
std::vector<int> mChildren;
+
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers);
};
} // namespace statsd
diff --git a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
index 86b148dd8657..423da5bd3cf8 100644
--- a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
@@ -50,6 +50,14 @@ bool SimpleAtomMatchingTracker::init(const vector<AtomMatcher>& allAtomMatchers,
return mInitialized;
}
+bool SimpleAtomMatchingTracker::onConfigUpdated(
+ const AtomMatcher& matcher, const int index,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+ mIndex = index;
+ // Do not need to update mMatcher since the matcher must be identical across the update.
+ return mInitialized;
+}
+
void SimpleAtomMatchingTracker::onLogEvent(
const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
vector<MatchingState>& matcherResults) {
diff --git a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h
index 49cbe09f8482..b67e6c20e8f1 100644
--- a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h
+++ b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h
@@ -28,7 +28,7 @@ namespace android {
namespace os {
namespace statsd {
-class SimpleAtomMatchingTracker : public virtual AtomMatchingTracker {
+class SimpleAtomMatchingTracker : public AtomMatchingTracker {
public:
SimpleAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash,
const SimpleAtomMatcher& matcher, const sp<UidMap>& uidMap);
@@ -40,6 +40,9 @@ public:
const std::unordered_map<int64_t, int>& matcherMap,
std::vector<bool>& stack) override;
+ bool onConfigUpdated(const AtomMatcher& matcher, const int index,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
+
void onLogEvent(const LogEvent& event,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
std::vector<MatchingState>& matcherResults) override;
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index a9ae5a4e854d..0983dc0b2c83 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -148,8 +148,13 @@ bool updateAtomTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
(long long)id);
return false;
}
- const int oldIndex = oldAtomMatchingTrackerIt->second;
- newAtomMatchingTrackers.push_back(oldAtomMatchingTrackers[oldIndex]);
+ const sp<AtomMatchingTracker>& tracker =
+ oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second];
+ if (!tracker->onConfigUpdated(matcherProtos[i], i, newAtomMatchingTrackerMap)) {
+ ALOGW("Config update failed for matcher %lld", (long long)id);
+ return false;
+ }
+ newAtomMatchingTrackers.push_back(tracker);
break;
}
case UPDATE_REPLACE: {
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index daf67e93557c..e40fbdb250f1 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -303,8 +303,7 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config,
const int conditionTrackerCount = config.predicate_size();
conditionConfigs.reserve(conditionTrackerCount);
allConditionTrackers.reserve(conditionTrackerCount);
- initialConditionCache.reserve(conditionTrackerCount);
- std::fill(initialConditionCache.begin(), initialConditionCache.end(), ConditionState::kUnknown);
+ initialConditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated);
for (int i = 0; i < conditionTrackerCount; i++) {
const Predicate& condition = config.predicate(i);
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index f6d30618ee15..8c698eb15d8d 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -23,6 +23,7 @@
#include <vector>
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "src/matchers/CombinationAtomMatchingTracker.h"
#include "src/metrics/parsing_utils/metrics_manager_util.h"
#include "tests/statsd_test_util.h"
@@ -370,13 +371,40 @@ TEST_F(ConfigUpdateTest, TestUpdateMatchers) {
EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination2Id)],
newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination2Id)]);
- // Validation, make sure the matchers have the proper ids. Could do more checks here.
+ // Validation, make sure the matchers have the proper ids/indices. Could do more checks here.
EXPECT_EQ(newAtomMatchingTrackers[0]->getId(), combination3Id);
+ EXPECT_EQ(newAtomMatchingTrackers[0]->mIndex, 0);
EXPECT_EQ(newAtomMatchingTrackers[1]->getId(), simple2Id);
+ EXPECT_EQ(newAtomMatchingTrackers[1]->mIndex, 1);
EXPECT_EQ(newAtomMatchingTrackers[2]->getId(), combination2Id);
+ EXPECT_EQ(newAtomMatchingTrackers[2]->mIndex, 2);
EXPECT_EQ(newAtomMatchingTrackers[3]->getId(), simple1Id);
+ EXPECT_EQ(newAtomMatchingTrackers[3]->mIndex, 3);
EXPECT_EQ(newAtomMatchingTrackers[4]->getId(), simple4Id);
+ EXPECT_EQ(newAtomMatchingTrackers[4]->mIndex, 4);
EXPECT_EQ(newAtomMatchingTrackers[5]->getId(), combination1Id);
+ EXPECT_EQ(newAtomMatchingTrackers[5]->mIndex, 5);
+
+ // Verify child indices of Combination Matchers are correct.
+ CombinationAtomMatchingTracker* combinationTracker1 =
+ static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[5].get());
+ vector<int>* childMatchers = &combinationTracker1->mChildren;
+ EXPECT_EQ(childMatchers->size(), 1);
+ EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end());
+
+ CombinationAtomMatchingTracker* combinationTracker2 =
+ static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[2].get());
+ childMatchers = &combinationTracker2->mChildren;
+ EXPECT_EQ(childMatchers->size(), 2);
+ EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end());
+ EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end());
+
+ CombinationAtomMatchingTracker* combinationTracker3 =
+ static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[0].get());
+ childMatchers = &combinationTracker3->mChildren;
+ EXPECT_EQ(childMatchers->size(), 2);
+ EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end());
+ EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 4), childMatchers->end());
}
} // namespace statsd
diff --git a/cmds/uinput/Android.bp b/cmds/uinput/Android.bp
new file mode 100644
index 000000000000..0d7fed2a15c7
--- /dev/null
+++ b/cmds/uinput/Android.bp
@@ -0,0 +1,18 @@
+// Copyright 2020 The Android Open Source Project
+//
+
+java_binary {
+ name: "uinput",
+ wrapper: "uinput",
+ srcs: ["**/*.java",
+ ":uinputcommand_aidl"
+ ],
+ required: ["libuinputcommand_jni"],
+}
+
+filegroup {
+ name: "uinputcommand_aidl",
+ srcs: [
+ "src/com/android/commands/uinput/InputAbsInfo.aidl",
+ ],
+} \ No newline at end of file
diff --git a/cmds/uinput/MODULE_LICENSE_APACHE2 b/cmds/uinput/MODULE_LICENSE_APACHE2
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/cmds/uinput/MODULE_LICENSE_APACHE2
diff --git a/cmds/uinput/NOTICE b/cmds/uinput/NOTICE
new file mode 100644
index 000000000000..c5b1efa7aac7
--- /dev/null
+++ b/cmds/uinput/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md
new file mode 100644
index 000000000000..47e1dad9ccd6
--- /dev/null
+++ b/cmds/uinput/README.md
@@ -0,0 +1,166 @@
+# Usage
+## Two options to use the uinput command:
+### 1. Interactive through stdin:
+type `uinput -` into the terminal, then type/paste commands to send to the binary.
+Use Ctrl+D to signal end of stream to the binary (EOF).
+
+This mode can be also used from an app to send uinput events.
+For an example, see the cts test case at: [InputTestCase.java][2]
+
+When using another program to control uinput in interactive mode, registering a
+new input device (for example, a bluetooth joystick) should be the first step.
+After the device is added, you need to wait for the _onInputDeviceAdded_
+(see [InputDeviceListener][1]) notification before issuing commands
+to the device.
+Failure to do so will cause missed events and inconsistent behavior.
+
+### 2. Using a file as an input:
+type `uinput <filename>`, and the file will be used an an input to the binary.
+You must add a sufficient delay after a "register" command to ensure device
+is ready. The interactive mode is the recommended method of communicating
+with the uinput binary.
+
+All of the input commands should be in pseudo-JSON format as documented below.
+See examples [here][3].
+
+The file can have multiple commands one after the other (which is not strictly
+legal JSON format, as this would imply multiple root elements).
+
+## Command description
+
+1. `register`
+Register a new uinput device
+
+| Field | Type | Description |
+|:-------------:|:-------------:|:-------------------------- |
+| id | integer | Device id |
+| command | string | Must be set to "register" |
+| name | string | Device name |
+| vid | 16-bit integer| Vendor id |
+| pid | 16-bit integer| Product id |
+| bus | string | Bus that device should use |
+| configuration | int array | uinput device configuration|
+| ff_effects_max| integer | ff_effects_max value |
+| abs_info | array | ABS axes information |
+
+Device ID is used for matching the subsequent commands to a specific device
+to avoid ambiguity when multiple devices are registered.
+
+Device bus is used to determine how the uinput device is connected to the host.
+The options are "usb" and "bluetooth".
+
+Device configuration is used to configure uinput device. "type" field provides the UI_SET_*
+control code, and data is a vector of control values to be sent to uinput device, depends on
+the control code.
+
+| Field | Type | Description |
+|:-------------:|:-------------:|:-------------------------- |
+| type | integer | UI_SET_ control type |
+| data | int array | control values |
+
+Device ff_effects_max must be provided if FFBIT is set.
+
+Device abs_info fields are provided to set the device axes information. It is an array of below
+objects:
+| Field | Type | Description |
+|:-------------:|:-------------:|:-------------------------- |
+| code | integer | Axis code |
+| info | object | ABS information object |
+
+ABS information object is defined as below:
+| Field | Type | Description |
+|:-------------:|:-------------:|:-------------------------- |
+| value | integer | Latest reported value |
+| minimum | integer | Minimum value for the axis |
+| maximum | integer | Maximum value for the axis |
+| fuzz | integer | fuzz value for noise filter|
+| flat | integer | values to be discarded |
+| resolution | integer | resolution of axis |
+
+See [struct input_absinfo][4]) definitions.
+
+Example:
+```json
+
+{
+ "id": 1,
+ "command": "register",
+ "name": "Keyboard (Test)",
+ "vid": 0x18d2,
+ "pid": 0x2c42,
+ "bus": "usb",
+ "configuration":[
+ {"type":100, "data":[1, 21]}, // UI_SET_EVBIT : EV_KEY and EV_FF
+ {"type":101, "data":[11, 2, 3, 4]}, // UI_SET_KEYBIT : KEY_0 KEY_1 KEY_2 KEY_3
+ {"type":107, "data":[80]} // UI_SET_FFBIT : FF_RUMBLE
+ ],
+ "ff_effects_max" : 1,
+ "abs_info": [
+ {"code":1, "info": {"value":20, "minimum":-255,
+ "maximum":255, "fuzz":0, "flat":0, "resolution":1}
+ },
+ {"code":8, "info": {"value":-50, "minimum":-255,
+ "maximum":255, "fuzz":0, "flat":0, "resolution":1}
+ }
+ ]
+}
+
+```
+2. `delay`
+Add a delay to command processing
+
+| Field | Type | Description |
+|:-------------:|:-------------:|:-------------------------- |
+| id | integer | Device id |
+| command | string | Must be set to "delay" |
+| duration | integer | Delay in milliseconds |
+
+Example:
+```json
+{
+ "id": 1,
+ "command": "delay",
+ "duration": 10
+}
+```
+
+3. `inject`
+Send an array of uinput event packets [type, code, value] to the uinput device
+
+| Field | Type | Description |
+|:-------------:|:-------------:|:-------------------------- |
+| id | integer | Device id |
+| command | string | Must be set to "inject" |
+| events | integer array | events to inject |
+
+The "events" parameter is an array of integers, encapsulates evdev input_event type, code and value,
+see the example below.
+
+Example:
+```json
+{
+ "id": 1,
+ "command": "inject",
+ "events": [0x01, 0xb, 0x1, // EV_KEY, KEY_0, DOWN
+ 0x00, 0x00, 0x00, // EV_SYN, SYN_REPORT, 0
+ 0x01, 0x0b, 0x00, // EV_KEY, KEY_0, UP
+ 0x00, 0x00, 0x00, // EV_SYN, SYN_REPORT, 0
+ 0x01, 0x2, 0x1, // EV_KEY, KEY_1, DOWN
+ 0x00, 0x00, 0x01, // EV_SYN, SYN_REPORT, 0
+ 0x01, 0x02, 0x00, // EV_KEY, KEY_1, UP
+ 0x00, 0x00, 0x01 // EV_SYN, SYN_REPORT, 0
+ ]
+}
+```
+
+### Notes
+1. As soon as EOF is reached (either in interactive mode, or in file mode),
+the device that was created will be unregistered. There is no
+explicit command for unregistering a device.
+2. The `getevent` utility can used to print out the key events
+for debugging purposes.
+
+[1]: https://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html
+[2]: ../../../../cts/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
+[3]: ../../../../cts/tests/tests/hardware/res/raw/
+[4]: ../../../../bionic/libc/kernel/uapi/linux/input.h
diff --git a/cmds/uinput/jni/Android.bp b/cmds/uinput/jni/Android.bp
new file mode 100644
index 000000000000..199bbbd35274
--- /dev/null
+++ b/cmds/uinput/jni/Android.bp
@@ -0,0 +1,23 @@
+cc_library_shared {
+ name: "libuinputcommand_jni",
+
+ srcs: [
+ "com_android_commands_uinput_Device.cpp",
+ ":uinputcommand_aidl",
+ ],
+
+ shared_libs: [
+ "libandroid",
+ "libandroid_runtime_lazy",
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libnativehelper",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
new file mode 100644
index 000000000000..06fa2aac2c7e
--- /dev/null
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
@@ -0,0 +1,351 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "UinputCommandDevice"
+
+#include <linux/uinput.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <time.h>
+#include <unistd.h>
+#include <algorithm>
+#include <array>
+#include <cstdio>
+#include <cstring>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include <android/looper.h>
+#include <android_os_Parcel.h>
+#include <jni.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#include <android-base/stringprintf.h>
+
+#include "com_android_commands_uinput_Device.h"
+
+namespace android {
+namespace uinput {
+
+using src::com::android::commands::uinput::InputAbsInfo;
+
+static constexpr const char* UINPUT_PATH = "/dev/uinput";
+
+static struct {
+ jmethodID onDeviceConfigure;
+ jmethodID onDeviceVibrating;
+ jmethodID onDeviceError;
+} gDeviceCallbackClassInfo;
+
+static void checkAndClearException(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ ALOGE("An exception was thrown by callback '%s'.", methodName);
+ env->ExceptionClear();
+ }
+}
+
+DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback)
+ : mCallbackObject(env->NewGlobalRef(callback)) {
+ env->GetJavaVM(&mJavaVM);
+}
+
+DeviceCallback::~DeviceCallback() {
+ JNIEnv* env = getJNIEnv();
+ env->DeleteGlobalRef(mCallbackObject);
+}
+
+void DeviceCallback::onDeviceError() {
+ JNIEnv* env = getJNIEnv();
+ env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceError);
+ checkAndClearException(env, "onDeviceError");
+}
+
+void DeviceCallback::onDeviceConfigure(int handle) {
+ JNIEnv* env = getJNIEnv();
+ env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceConfigure, handle);
+ checkAndClearException(env, "onDeviceConfigure");
+}
+
+void DeviceCallback::onDeviceVibrating(int value) {
+ JNIEnv* env = getJNIEnv();
+ env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceVibrating, value);
+ checkAndClearException(env, "onDeviceVibrating");
+}
+
+JNIEnv* DeviceCallback::getJNIEnv() {
+ JNIEnv* env;
+ mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+ return env;
+}
+
+std::unique_ptr<UinputDevice> UinputDevice::open(int32_t id, const char* name, int32_t vid,
+ int32_t pid, uint16_t bus, uint32_t ffEffectsMax,
+ std::unique_ptr<DeviceCallback> callback) {
+ android::base::unique_fd fd(::open(UINPUT_PATH, O_RDWR | O_NONBLOCK | O_CLOEXEC));
+ if (!fd.ok()) {
+ ALOGE("Failed to open uinput: %s", strerror(errno));
+ return nullptr;
+ }
+
+ int32_t version;
+ ::ioctl(fd, UI_GET_VERSION, &version);
+ if (version < 5) {
+ ALOGE("Kernel version %d older than 5 is not supported", version);
+ return nullptr;
+ }
+
+ struct uinput_setup setupDescriptor;
+ memset(&setupDescriptor, 0, sizeof(setupDescriptor));
+ strlcpy(setupDescriptor.name, name, UINPUT_MAX_NAME_SIZE);
+ setupDescriptor.id.version = 1;
+ setupDescriptor.id.bustype = bus;
+ setupDescriptor.id.vendor = vid;
+ setupDescriptor.id.product = pid;
+ setupDescriptor.ff_effects_max = ffEffectsMax;
+
+ // Request device configuration.
+ callback->onDeviceConfigure(fd.get());
+
+ // register the input device
+ if (::ioctl(fd, UI_DEV_SETUP, &setupDescriptor)) {
+ ALOGE("UI_DEV_SETUP ioctl failed on fd %d: %s.", fd.get(), strerror(errno));
+ return nullptr;
+ }
+
+ if (::ioctl(fd, UI_DEV_CREATE) != 0) {
+ ALOGE("Unable to create uinput device: %s.", strerror(errno));
+ return nullptr;
+ }
+
+ // using 'new' to access non-public constructor
+ return std::unique_ptr<UinputDevice>(new UinputDevice(id, std::move(fd), std::move(callback)));
+}
+
+UinputDevice::UinputDevice(int32_t id, android::base::unique_fd fd,
+ std::unique_ptr<DeviceCallback> callback)
+ : mId(id), mFd(std::move(fd)), mDeviceCallback(std::move(callback)) {
+ ALooper* aLooper = ALooper_forThread();
+ if (aLooper == nullptr) {
+ ALOGE("Could not get ALooper, ALooper_forThread returned NULL");
+ aLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+ }
+ ALooper_addFd(
+ aLooper, mFd, 0, ALOOPER_EVENT_INPUT,
+ [](int, int events, void* data) {
+ UinputDevice* d = reinterpret_cast<UinputDevice*>(data);
+ return d->handleEvents(events);
+ },
+ reinterpret_cast<void*>(this));
+ ALOGI("uinput device %d created: version = %d, fd = %d", mId, UINPUT_VERSION, mFd.get());
+}
+
+UinputDevice::~UinputDevice() {
+ ::ioctl(mFd, UI_DEV_DESTROY);
+}
+
+void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
+ struct input_event event = {};
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ TIMESPEC_TO_TIMEVAL(&event.time, &ts);
+
+ if (::write(mFd, &event, sizeof(input_event)) < 0) {
+ ALOGE("Could not write event %" PRIu16 " %" PRIu16 " with value %" PRId32 " : %s", type,
+ code, value, strerror(errno));
+ }
+}
+
+int UinputDevice::handleEvents(int events) {
+ if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
+ ALOGE("uinput node was closed or an error occurred. events=0x%x", events);
+ mDeviceCallback->onDeviceError();
+ return 0;
+ }
+ struct input_event ev;
+ ssize_t ret = ::read(mFd, &ev, sizeof(ev));
+ if (ret < 0) {
+ ALOGE("Failed to read from uinput node: %s", strerror(errno));
+ mDeviceCallback->onDeviceError();
+ return 0;
+ }
+
+ switch (ev.type) {
+ case EV_UINPUT: {
+ if (ev.code == UI_FF_UPLOAD) {
+ struct uinput_ff_upload ff_upload;
+ ff_upload.request_id = ev.value;
+ ::ioctl(mFd, UI_BEGIN_FF_UPLOAD, &ff_upload);
+ ff_upload.retval = 0;
+ ::ioctl(mFd, UI_END_FF_UPLOAD, &ff_upload);
+ } else if (ev.code == UI_FF_ERASE) {
+ struct uinput_ff_erase ff_erase;
+ ff_erase.request_id = ev.value;
+ ::ioctl(mFd, UI_BEGIN_FF_ERASE, &ff_erase);
+ ff_erase.retval = 0;
+ ::ioctl(mFd, UI_END_FF_ERASE, &ff_erase);
+ }
+ break;
+ }
+ case EV_FF: {
+ ALOGI("EV_FF effect = %d value = %d", ev.code, ev.value);
+ mDeviceCallback->onDeviceVibrating(ev.value);
+ break;
+ }
+ default: {
+ ALOGI("Unhandled event type: %" PRIu32, ev.type);
+ break;
+ }
+ }
+
+ return 1;
+}
+
+} // namespace uinput
+
+std::vector<int32_t> toVector(JNIEnv* env, jintArray javaArray) {
+ std::vector<int32_t> data;
+ if (javaArray == nullptr) {
+ return data;
+ }
+
+ ScopedIntArrayRO scopedArray(env, javaArray);
+ size_t size = scopedArray.size();
+ data.reserve(size);
+ for (size_t i = 0; i < size; i++) {
+ data.push_back(static_cast<int32_t>(scopedArray[i]));
+ }
+ return data;
+}
+
+static jlong openUinputDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
+ jint pid, jint bus, jint ffEffectsMax, jobject callback) {
+ ScopedUtfChars name(env, rawName);
+ if (name.c_str() == nullptr) {
+ return 0;
+ }
+
+ std::unique_ptr<uinput::DeviceCallback> cb =
+ std::make_unique<uinput::DeviceCallback>(env, callback);
+
+ std::unique_ptr<uinput::UinputDevice> d =
+ uinput::UinputDevice::open(id, name.c_str(), vid, pid, bus, ffEffectsMax,
+ std::move(cb));
+ return reinterpret_cast<jlong>(d.release());
+}
+
+static void closeUinputDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
+ uinput::UinputDevice* d = reinterpret_cast<uinput::UinputDevice*>(ptr);
+ if (d != nullptr) {
+ delete d;
+ }
+}
+
+static void injectEvent(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint type, jint code,
+ jint value) {
+ uinput::UinputDevice* d = reinterpret_cast<uinput::UinputDevice*>(ptr);
+ if (d != nullptr) {
+ d->injectEvent(static_cast<uint16_t>(type), static_cast<uint16_t>(code),
+ static_cast<int32_t>(value));
+ } else {
+ ALOGE("Could not inject event, Device* is null!");
+ }
+}
+
+static void configure(JNIEnv* env, jclass /* clazz */, jint handle, jint code,
+ jintArray rawConfigs) {
+ std::vector<int32_t> configs = toVector(env, rawConfigs);
+ // Configure uinput device, with user specified code and value.
+ for (auto& config : configs) {
+ ::ioctl(static_cast<int>(handle), _IOW(UINPUT_IOCTL_BASE, code, int), config);
+ }
+}
+
+static void setAbsInfo(JNIEnv* env, jclass /* clazz */, jint handle, jint axisCode,
+ jobject infoObj) {
+ Parcel* parcel = parcelForJavaObject(env, infoObj);
+ uinput::InputAbsInfo info;
+
+ info.readFromParcel(parcel);
+
+ struct uinput_abs_setup absSetup;
+ absSetup.code = axisCode;
+ absSetup.absinfo.maximum = info.maximum;
+ absSetup.absinfo.minimum = info.minimum;
+ absSetup.absinfo.value = info.value;
+ absSetup.absinfo.fuzz = info.fuzz;
+ absSetup.absinfo.flat = info.flat;
+ absSetup.absinfo.resolution = info.resolution;
+
+ ::ioctl(static_cast<int>(handle), UI_ABS_SETUP, &absSetup);
+}
+
+static JNINativeMethod sMethods[] = {
+ {"nativeOpenUinputDevice",
+ "(Ljava/lang/String;IIIII"
+ "Lcom/android/commands/uinput/Device$DeviceCallback;)J",
+ reinterpret_cast<void*>(openUinputDevice)},
+ {"nativeInjectEvent", "(JIII)V", reinterpret_cast<void*>(injectEvent)},
+ {"nativeConfigure", "(II[I)V", reinterpret_cast<void*>(configure)},
+ {"nativeSetAbsInfo", "(IILandroid/os/Parcel;)V", reinterpret_cast<void*>(setAbsInfo)},
+ {"nativeCloseUinputDevice", "(J)V", reinterpret_cast<void*>(closeUinputDevice)},
+};
+
+int register_com_android_commands_uinput_Device(JNIEnv* env) {
+ jclass clazz = env->FindClass("com/android/commands/uinput/Device$DeviceCallback");
+ if (clazz == nullptr) {
+ ALOGE("Unable to find class 'DeviceCallback'");
+ return JNI_ERR;
+ }
+
+ uinput::gDeviceCallbackClassInfo.onDeviceConfigure =
+ env->GetMethodID(clazz, "onDeviceConfigure", "(I)V");
+ uinput::gDeviceCallbackClassInfo.onDeviceVibrating =
+ env->GetMethodID(clazz, "onDeviceVibrating", "(I)V");
+ uinput::gDeviceCallbackClassInfo.onDeviceError =
+ env->GetMethodID(clazz, "onDeviceError", "()V");
+ if (uinput::gDeviceCallbackClassInfo.onDeviceConfigure == nullptr ||
+ uinput::gDeviceCallbackClassInfo.onDeviceError == nullptr ||
+ uinput::gDeviceCallbackClassInfo.onDeviceVibrating == nullptr) {
+ ALOGE("Unable to obtain onDeviceConfigure or onDeviceError or onDeviceVibrating methods");
+ return JNI_ERR;
+ }
+ return jniRegisterNativeMethods(env, "com/android/commands/uinput/Device", sMethods,
+ NELEM(sMethods));
+}
+
+} // namespace android
+
+jint JNI_OnLoad(JavaVM* jvm, void*) {
+ JNIEnv* env = nullptr;
+ if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) {
+ return JNI_ERR;
+ }
+
+ if (android::register_com_android_commands_uinput_Device(env) < 0) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_6;
+}
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.h b/cmds/uinput/jni/com_android_commands_uinput_Device.h
new file mode 100644
index 000000000000..5a9a06cfb32e
--- /dev/null
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#include <memory>
+#include <vector>
+
+#include <jni.h>
+#include <linux/input.h>
+
+#include <android-base/unique_fd.h>
+#include "src/com/android/commands/uinput/InputAbsInfo.h"
+
+namespace android {
+namespace uinput {
+
+class DeviceCallback {
+public:
+ DeviceCallback(JNIEnv* env, jobject callback);
+ ~DeviceCallback();
+
+ void onDeviceOpen();
+ void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
+ void onDeviceOutput(const std::vector<uint8_t>& data);
+ void onDeviceConfigure(int handle);
+ void onDeviceVibrating(int value);
+ void onDeviceError();
+
+private:
+ JNIEnv* getJNIEnv();
+ jobject mCallbackObject;
+ JavaVM* mJavaVM;
+};
+
+class UinputDevice {
+public:
+ static std::unique_ptr<UinputDevice> open(int32_t id, const char* name, int32_t vid,
+ int32_t pid, uint16_t bus, uint32_t ff_effects_max,
+ std::unique_ptr<DeviceCallback> callback);
+
+ virtual ~UinputDevice();
+
+ void injectEvent(uint16_t type, uint16_t code, int32_t value);
+ int handleEvents(int events);
+
+private:
+ UinputDevice(int32_t id, android::base::unique_fd fd, std::unique_ptr<DeviceCallback> callback);
+
+ int32_t mId;
+ android::base::unique_fd mFd;
+ std::unique_ptr<DeviceCallback> mDeviceCallback;
+};
+
+} // namespace uinput
+} // namespace android
diff --git a/cmds/uinput/src/com/android/commands/uinput/Device.java b/cmds/uinput/src/com/android/commands/uinput/Device.java
new file mode 100644
index 000000000000..62bee7b964bd
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/Device.java
@@ -0,0 +1,232 @@
+/*
+ * 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.commands.uinput;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.os.SomeArgs;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import src.com.android.commands.uinput.InputAbsInfo;
+
+/**
+ * Device class defines uinput device interfaces of device operations, for device open, close,
+ * configuration, events injection.
+ */
+public class Device {
+ private static final String TAG = "UinputDevice";
+
+ private static final int MSG_OPEN_UINPUT_DEVICE = 1;
+ private static final int MSG_CLOSE_UINPUT_DEVICE = 2;
+ private static final int MSG_INJECT_EVENT = 3;
+
+ private final int mId;
+ private final HandlerThread mThread;
+ private final DeviceHandler mHandler;
+ // mConfiguration is sparse array of ioctl code and array of values.
+ private final SparseArray<int[]> mConfiguration;
+ private final SparseArray<InputAbsInfo> mAbsInfo;
+ private final OutputStream mOutputStream;
+ private final Object mCond = new Object();
+ private long mTimeToSend;
+
+ static {
+ System.loadLibrary("uinputcommand_jni");
+ }
+
+ private static native long nativeOpenUinputDevice(String name, int id, int vid, int pid,
+ int bus, int ffEffectsMax, DeviceCallback callback);
+ private static native void nativeCloseUinputDevice(long ptr);
+ private static native void nativeInjectEvent(long ptr, int type, int code, int value);
+ private static native void nativeConfigure(int handle, int code, int[] configs);
+ private static native void nativeSetAbsInfo(int handle, int axisCode, Parcel axisParcel);
+
+ public Device(int id, String name, int vid, int pid, int bus,
+ SparseArray<int[]> configuration, int ffEffectsMax,
+ SparseArray<InputAbsInfo> absInfo) {
+ mId = id;
+ mThread = new HandlerThread("UinputDeviceHandler");
+ mThread.start();
+ mHandler = new DeviceHandler(mThread.getLooper());
+ mConfiguration = configuration;
+ mAbsInfo = absInfo;
+ mOutputStream = System.out;
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = id;
+ args.argi2 = vid;
+ args.argi3 = pid;
+ args.argi4 = bus;
+ args.argi5 = ffEffectsMax;
+ if (name != null) {
+ args.arg1 = name;
+ } else {
+ args.arg1 = id + ":" + vid + ":" + pid;
+ }
+
+ mHandler.obtainMessage(MSG_OPEN_UINPUT_DEVICE, args).sendToTarget();
+ mTimeToSend = SystemClock.uptimeMillis();
+ }
+
+ /**
+ * Inject uinput events to device
+ *
+ * @param events Array of raw uinput events.
+ */
+ public void injectEvent(int[] events) {
+ // if two messages are sent at identical time, they will be processed in order received
+ Message msg = mHandler.obtainMessage(MSG_INJECT_EVENT, events);
+ mHandler.sendMessageAtTime(msg, mTimeToSend);
+ }
+
+ /**
+ * Impose a delay to the device for execution.
+ *
+ * @param delay Time to delay in unit of milliseconds.
+ */
+ public void addDelay(int delay) {
+ mTimeToSend = Math.max(SystemClock.uptimeMillis(), mTimeToSend) + delay;
+ }
+
+ /**
+ * Close an uinput device.
+ *
+ */
+ public void close() {
+ Message msg = mHandler.obtainMessage(MSG_CLOSE_UINPUT_DEVICE);
+ mHandler.sendMessageAtTime(msg, Math.max(SystemClock.uptimeMillis(), mTimeToSend) + 1);
+ try {
+ synchronized (mCond) {
+ mCond.wait();
+ }
+ } catch (InterruptedException ignore) {
+ }
+ }
+
+ private class DeviceHandler extends Handler {
+ private long mPtr;
+ private int mBarrierToken;
+
+ DeviceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_OPEN_UINPUT_DEVICE:
+ SomeArgs args = (SomeArgs) msg.obj;
+ mPtr = nativeOpenUinputDevice((String) args.arg1, args.argi1, args.argi2,
+ args.argi3, args.argi4, args.argi5,
+ new DeviceCallback());
+ break;
+ case MSG_INJECT_EVENT:
+ if (mPtr != 0) {
+ int[] events = (int[]) msg.obj;
+ for (int pos = 0; pos + 2 < events.length; pos += 3) {
+ nativeInjectEvent(mPtr, events[pos], events[pos + 1], events[pos + 2]);
+ }
+ }
+ break;
+ case MSG_CLOSE_UINPUT_DEVICE:
+ if (mPtr != 0) {
+ nativeCloseUinputDevice(mPtr);
+ getLooper().quitSafely();
+ mPtr = 0;
+ } else {
+ Log.e(TAG, "Tried to close already closed device.");
+ }
+ Log.i(TAG, "Device closed.");
+ synchronized (mCond) {
+ mCond.notify();
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown device message");
+ }
+ }
+
+ public void pauseEvents() {
+ mBarrierToken = getLooper().myQueue().postSyncBarrier();
+ }
+
+ public void resumeEvents() {
+ getLooper().myQueue().removeSyncBarrier(mBarrierToken);
+ mBarrierToken = 0;
+ }
+ }
+
+ private class DeviceCallback {
+ public void onDeviceOpen() {
+ mHandler.resumeEvents();
+ }
+
+ public void onDeviceConfigure(int handle) {
+ for (int i = 0; i < mConfiguration.size(); i++) {
+ int key = mConfiguration.keyAt(i);
+ int[] data = mConfiguration.get(key);
+ nativeConfigure(handle, key, data);
+ }
+
+ if (mAbsInfo != null) {
+ for (int i = 0; i < mAbsInfo.size(); i++) {
+ int key = mAbsInfo.keyAt(i);
+ InputAbsInfo info = mAbsInfo.get(key);
+ Parcel parcel = Parcel.obtain();
+ info.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ nativeSetAbsInfo(handle, key, parcel);
+ }
+ }
+ }
+
+ public void onDeviceVibrating(int value) {
+ JSONObject json = new JSONObject();
+ try {
+ json.put("reason", "vibrating");
+ json.put("id", mId);
+ json.put("status", value);
+ } catch (JSONException e) {
+ throw new RuntimeException("Could not create JSON object ", e);
+ }
+ try {
+ mOutputStream.write(json.toString().getBytes());
+ mOutputStream.flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void onDeviceError() {
+ Log.e(TAG, "Device error occurred, closing /dev/uinput");
+ Message msg = mHandler.obtainMessage(MSG_CLOSE_UINPUT_DEVICE);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
+ }
+ }
+}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
new file mode 100644
index 000000000000..c4ba05054eda
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -0,0 +1,454 @@
+/*
+ * 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.commands.uinput;
+
+import android.util.JsonReader;
+import android.util.JsonToken;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import src.com.android.commands.uinput.InputAbsInfo;
+
+/**
+ * An event is a JSON file defined action event to instruct uinput to perform a command like
+ * device registration or uinput events injection.
+ */
+public class Event {
+ private static final String TAG = "UinputEvent";
+
+ public static final String COMMAND_REGISTER = "register";
+ public static final String COMMAND_DELAY = "delay";
+ public static final String COMMAND_INJECT = "inject";
+ private static final int ABS_CNT = 64;
+
+ // These constants come from "include/uapi/linux/input.h" in the kernel
+ enum Bus {
+ USB(0x03), BLUETOOTH(0x05);
+ private final int mValue;
+
+ Bus(int value) {
+ mValue = value;
+ }
+
+ int getValue() {
+ return mValue;
+ }
+ }
+
+ private int mId;
+ private String mCommand;
+ private String mName;
+ private int mVid;
+ private int mPid;
+ private Bus mBus;
+ private int[] mInjections;
+ private SparseArray<int[]> mConfiguration;
+ private int mDuration;
+ private int mFfEffectsMax = 0;
+ private SparseArray<InputAbsInfo> mAbsInfo;
+
+ public int getId() {
+ return mId;
+ }
+
+ public String getCommand() {
+ return mCommand;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getVendorId() {
+ return mVid;
+ }
+
+ public int getProductId() {
+ return mPid;
+ }
+
+ public int getBus() {
+ return mBus.getValue();
+ }
+
+ public int[] getInjections() {
+ return mInjections;
+ }
+
+ public SparseArray<int[]> getConfiguration() {
+ return mConfiguration;
+ }
+
+ public int getDuration() {
+ return mDuration;
+ }
+
+ public int getFfEffectsMax() {
+ return mFfEffectsMax;
+ }
+
+ public SparseArray<InputAbsInfo> getAbsInfo() {
+ return mAbsInfo;
+ }
+
+ /**
+ * Convert an event to String.
+ */
+ public String toString() {
+ return "Event{id=" + mId
+ + ", command=" + mCommand
+ + ", name=" + mName
+ + ", vid=" + mVid
+ + ", pid=" + mPid
+ + ", bus=" + mBus
+ + ", events=" + Arrays.toString(mInjections)
+ + ", configuration=" + mConfiguration
+ + ", duration=" + mDuration
+ + ", ff_effects_max=" + mFfEffectsMax
+ + "}";
+ }
+
+ private static class Builder {
+ private Event mEvent;
+
+ Builder() {
+ mEvent = new Event();
+ }
+
+ public void setId(int id) {
+ mEvent.mId = id;
+ }
+
+ private void setCommand(String command) {
+ mEvent.mCommand = command;
+ }
+
+ public void setName(String name) {
+ mEvent.mName = name;
+ }
+
+ public void setInjections(int[] events) {
+ mEvent.mInjections = events;
+ }
+
+ public void setConfiguration(SparseArray<int[]> configuration) {
+ mEvent.mConfiguration = configuration;
+ }
+
+ public void setVid(int vid) {
+ mEvent.mVid = vid;
+ }
+
+ public void setPid(int pid) {
+ mEvent.mPid = pid;
+ }
+
+ public void setBus(Bus bus) {
+ mEvent.mBus = bus;
+ }
+
+ public void setDuration(int duration) {
+ mEvent.mDuration = duration;
+ }
+
+ public void setFfEffectsMax(int ffEffectsMax) {
+ mEvent.mFfEffectsMax = ffEffectsMax;
+ }
+
+ public void setAbsInfo(SparseArray<InputAbsInfo> absInfo) {
+ mEvent.mAbsInfo = absInfo;
+ }
+
+ public Event build() {
+ if (mEvent.mId == -1) {
+ throw new IllegalStateException("No event id");
+ } else if (mEvent.mCommand == null) {
+ throw new IllegalStateException("Event does not contain a command");
+ }
+ if (COMMAND_REGISTER.equals(mEvent.mCommand)) {
+ if (mEvent.mConfiguration == null) {
+ throw new IllegalStateException(
+ "Device registration is missing configuration");
+ }
+ } else if (COMMAND_DELAY.equals(mEvent.mCommand)) {
+ if (mEvent.mDuration <= 0) {
+ throw new IllegalStateException("Delay has missing or invalid duration");
+ }
+ } else if (COMMAND_INJECT.equals(mEvent.mCommand)) {
+ if (mEvent.mInjections == null) {
+ throw new IllegalStateException("Inject command is missing injection data");
+ }
+ } else {
+ throw new IllegalStateException("Unknown command " + mEvent.mCommand);
+ }
+ return mEvent;
+ }
+ }
+
+ /**
+ * A class that parses the JSON event format from an input stream to build device events.
+ */
+ public static class Reader {
+ private JsonReader mReader;
+
+ public Reader(InputStreamReader in) {
+ mReader = new JsonReader(in);
+ mReader.setLenient(true);
+ }
+
+ /**
+ * Get next event entry from JSON file reader.
+ */
+ public Event getNextEvent() throws IOException {
+ Event e = null;
+ while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) {
+ Event.Builder eb = new Event.Builder();
+ try {
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "id":
+ eb.setId(readInt());
+ break;
+ case "command":
+ eb.setCommand(mReader.nextString());
+ break;
+ case "name":
+ eb.setName(mReader.nextString());
+ break;
+ case "vid":
+ eb.setVid(readInt());
+ break;
+ case "pid":
+ eb.setPid(readInt());
+ break;
+ case "bus":
+ eb.setBus(readBus());
+ break;
+ case "events":
+ int[] injections = readIntList().stream()
+ .mapToInt(Integer::intValue).toArray();
+ eb.setInjections(injections);
+ break;
+ case "configuration":
+ eb.setConfiguration(readConfiguration());
+ break;
+ case "ff_effects_max":
+ eb.setFfEffectsMax(readInt());
+ break;
+ case "abs_info":
+ eb.setAbsInfo(readAbsInfoArray());
+ break;
+ case "duration":
+ eb.setDuration(readInt());
+ break;
+ default:
+ mReader.skipValue();
+ }
+ }
+ mReader.endObject();
+ } catch (IllegalStateException ex) {
+ error("Error reading in object, ignoring.", ex);
+ consumeRemainingElements();
+ mReader.endObject();
+ continue;
+ }
+ e = eb.build();
+ }
+
+ return e;
+ }
+
+ private ArrayList<Integer> readIntList() throws IOException {
+ ArrayList<Integer> data = new ArrayList<Integer>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ data.add(Integer.decode(mReader.nextString()));
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return data;
+ }
+
+ private byte[] readData() throws IOException {
+ ArrayList<Integer> data = readIntList();
+ byte[] rawData = new byte[data.size()];
+ for (int i = 0; i < data.size(); i++) {
+ int d = data.get(i);
+ if ((d & 0xFF) != d) {
+ throw new IllegalStateException("Invalid data, all values must be byte-sized");
+ }
+ rawData[i] = (byte) d;
+ }
+ return rawData;
+ }
+
+ private int readInt() throws IOException {
+ String val = mReader.nextString();
+ return Integer.decode(val);
+ }
+
+ private Bus readBus() throws IOException {
+ String val = mReader.nextString();
+ return Bus.valueOf(val.toUpperCase());
+ }
+
+ private SparseArray<int[]> readConfiguration()
+ throws IllegalStateException, IOException {
+ SparseArray<int[]> configuration = new SparseArray<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ int type = 0;
+ int[] data = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "type":
+ type = readInt();
+ break;
+ case "data":
+ data = readIntList().stream()
+ .mapToInt(Integer::intValue).toArray();
+ break;
+ default:
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException(
+ "Invalid key in device configuration: " + name);
+ }
+ }
+ mReader.endObject();
+ if (data != null) {
+ configuration.put(type, data);
+ }
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return configuration;
+ }
+
+ private InputAbsInfo readAbsInfo() throws IllegalStateException, IOException {
+ InputAbsInfo absInfo = new InputAbsInfo();
+ try {
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "value":
+ absInfo.value = readInt();
+ break;
+ case "minimum":
+ absInfo.minimum = readInt();
+ break;
+ case "maximum":
+ absInfo.maximum = readInt();
+ break;
+ case "fuzz":
+ absInfo.fuzz = readInt();
+ break;
+ case "flat":
+ absInfo.flat = readInt();
+ break;
+ case "resolution":
+ absInfo.resolution = readInt();
+ break;
+ default:
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in abs info: " + name);
+ }
+ }
+ mReader.endObject();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return absInfo;
+ }
+
+ private SparseArray<InputAbsInfo> readAbsInfoArray()
+ throws IllegalStateException, IOException {
+ SparseArray<InputAbsInfo> infoArray = new SparseArray<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ int type = 0;
+ InputAbsInfo absInfo = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "code":
+ type = readInt();
+ break;
+ case "info":
+ absInfo = readAbsInfo();
+ break;
+ default:
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in abs info array: "
+ + name);
+ }
+ }
+ mReader.endObject();
+ if (absInfo != null) {
+ infoArray.put(type, absInfo);
+ }
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return infoArray;
+ }
+
+ private void consumeRemainingElements() throws IOException {
+ while (mReader.hasNext()) {
+ mReader.skipValue();
+ }
+ }
+ }
+
+ private static void error(String msg, Exception e) {
+ System.out.println(msg);
+ Log.e(TAG, msg);
+ if (e != null) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+}
diff --git a/cmds/uinput/src/com/android/commands/uinput/InputAbsInfo.aidl b/cmds/uinput/src/com/android/commands/uinput/InputAbsInfo.aidl
new file mode 100644
index 000000000000..88c57f2c8965
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/InputAbsInfo.aidl
@@ -0,0 +1,26 @@
+/*
+**
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+package src.com.android.commands.uinput;
+
+parcelable InputAbsInfo {
+ int value;
+ int minimum;
+ int maximum;
+ int fuzz;
+ int flat;
+ int resolution;
+}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
new file mode 100644
index 000000000000..f7601a2f7c07
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
@@ -0,0 +1,140 @@
+/*
+ * 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.commands.uinput;
+
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Uinput class encapsulates execution of "uinput" command. It parses the provided input stream
+ * parameters as JSON file format, extract event entries and perform commands of event entries.
+ * Uinput device will be created when performing registration command and used to inject events.
+ */
+public class Uinput {
+ private static final String TAG = "UINPUT";
+
+ private final Event.Reader mReader;
+ private final SparseArray<Device> mDevices;
+
+ private static void usage() {
+ error("Usage: uinput [FILE]");
+ }
+
+ /**
+ * Commandline "uinput" binary main entry
+ */
+ public static void main(String[] args) {
+ if (args.length != 1) {
+ usage();
+ System.exit(1);
+ }
+
+ InputStream stream = null;
+ try {
+ if (args[0].equals("-")) {
+ stream = System.in;
+ } else {
+ File f = new File(args[0]);
+ stream = new FileInputStream(f);
+ }
+ (new Uinput(stream)).run();
+ } catch (Exception e) {
+ error("Uinput injection failed.", e);
+ System.exit(1);
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ private Uinput(InputStream in) {
+ mDevices = new SparseArray<Device>();
+ try {
+ mReader = new Event.Reader(new InputStreamReader(in, "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void run() {
+ try {
+ Event e = null;
+ while ((e = mReader.getNextEvent()) != null) {
+ process(e);
+ }
+ } catch (IOException ex) {
+ error("Error reading in events.", ex);
+ }
+
+ for (int i = 0; i < mDevices.size(); i++) {
+ mDevices.valueAt(i).close();
+ }
+ }
+
+ private void process(Event e) {
+ final int index = mDevices.indexOfKey(e.getId());
+ if (index >= 0) {
+ Device d = mDevices.valueAt(index);
+ if (Event.COMMAND_DELAY.equals(e.getCommand())) {
+ d.addDelay(e.getDuration());
+ } else if (Event.COMMAND_INJECT.equals(e.getCommand())) {
+ d.injectEvent(e.getInjections());
+ } else {
+ if (Event.COMMAND_REGISTER.equals(e.getCommand())) {
+ error("Device id=" + e.getId() + " is already registered. Ignoring event.");
+ } else {
+ error("Unknown command \"" + e.getCommand() + "\". Ignoring event.");
+ }
+ }
+ } else if (Event.COMMAND_REGISTER.equals(e.getCommand())) {
+ registerDevice(e);
+ } else {
+ Log.e(TAG, "Unknown device id specified. Ignoring event.");
+ }
+ }
+
+ private void registerDevice(Event e) {
+ if (!Event.COMMAND_REGISTER.equals(e.getCommand())) {
+ throw new IllegalStateException(
+ "Tried to send command \"" + e.getCommand() + "\" to an unregistered device!");
+ }
+ int id = e.getId();
+ Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(),
+ e.getConfiguration(), e.getFfEffectsMax(), e.getAbsInfo());
+ mDevices.append(id, d);
+ }
+
+ private static void error(String msg) {
+ error(msg, null);
+ }
+
+ private static void error(String msg, Exception e) {
+ Log.e(TAG, msg);
+ if (e != null) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+}
diff --git a/cmds/uinput/uinput b/cmds/uinput/uinput
new file mode 100755
index 000000000000..ab2770ee2043
--- /dev/null
+++ b/cmds/uinput/uinput
@@ -0,0 +1,9 @@
+#!/system/bin/sh
+
+# Preload the native portion libuinputcommand_jni.so to bypass the dependency
+# checks in the Java classloader, which prohibit dependencies that aren't
+# listed in system/core/rootdir/etc/public.libraries.android.txt.
+export LD_PRELOAD=libuinputcommand_jni.so
+
+export CLASSPATH=/system/framework/uinput.jar
+exec app_process /system/bin com.android.commands.uinput.Uinput "$@"
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 95136bb46339..00557114bab0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1145,9 +1145,24 @@ public class AppOpsManager {
/** @hide */
public static final int OP_NO_ISOLATED_STORAGE = AppProtoEnums.APP_OP_NO_ISOLATED_STORAGE;
+ /**
+ * Phone call is using microphone
+ *
+ * @hide
+ */
+ // TODO: Add as AppProtoEnums
+ public static final int OP_PHONE_CALL_MICROPHONE = 100;
+ /**
+ * Phone call is using camera
+ *
+ * @hide
+ */
+ // TODO: Add as AppProtoEnums
+ public static final int OP_PHONE_CALL_CAMERA = 101;
+
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 100;
+ public static final int _NUM_OP = 102;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1469,6 +1484,19 @@ public class AppOpsManager {
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
+ /**
+ * Phone call is using microphone
+ *
+ * @hide
+ */
+ public static final String OPSTR_PHONE_CALL_MICROPHONE = "android:phone_call_microphone";
+ /**
+ * Phone call is using camera
+ *
+ * @hide
+ */
+ public static final String OPSTR_PHONE_CALL_CAMERA = "android:phone_call_camera";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1658,6 +1686,8 @@ public class AppOpsManager {
OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED
OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
OP_NO_ISOLATED_STORAGE, // NO_ISOLATED_STORAGE
+ OP_PHONE_CALL_MICROPHONE, // OP_PHONE_CALL_MICROPHONE
+ OP_PHONE_CALL_CAMERA, // OP_PHONE_CALL_CAMERA
};
/**
@@ -1764,6 +1794,8 @@ public class AppOpsManager {
OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER,
OPSTR_NO_ISOLATED_STORAGE,
+ OPSTR_PHONE_CALL_MICROPHONE,
+ OPSTR_PHONE_CALL_CAMERA,
};
/**
@@ -1871,6 +1903,8 @@ public class AppOpsManager {
"AUTO_REVOKE_PERMISSIONS_IF_UNUSED",
"AUTO_REVOKE_MANAGED_BY_INSTALLER",
"NO_ISOLATED_STORAGE",
+ "PHONE_CALL_MICROPHONE",
+ "PHONE_CALL_CAMERA",
};
/**
@@ -1979,6 +2013,8 @@ public class AppOpsManager {
null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
null, // no permission for OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
null, // no permission for OP_NO_ISOLATED_STORAGE
+ null, // no permission for OP_PHONE_CALL_MICROPHONE
+ null, // no permission for OP_PHONE_CALL_CAMERA
};
/**
@@ -2087,6 +2123,8 @@ public class AppOpsManager {
null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
null, // NO_ISOLATED_STORAGE
+ null, // PHONE_CALL_MICROPHONE
+ null, // PHONE_CALL_MICROPHONE
};
/**
@@ -2194,6 +2232,8 @@ public class AppOpsManager {
null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
null, // NO_ISOLATED_STORAGE
+ null, // PHONE_CALL_MICROPHONE
+ null, // PHONE_CALL_CAMERA
};
/**
@@ -2300,6 +2340,8 @@ public class AppOpsManager {
AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
AppOpsManager.MODE_ALLOWED, // OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
AppOpsManager.MODE_ERRORED, // OP_NO_ISOLATED_STORAGE
+ AppOpsManager.MODE_ALLOWED, // PHONE_CALL_MICROPHONE
+ AppOpsManager.MODE_ALLOWED, // PHONE_CALL_CAMERA
};
/**
@@ -2410,6 +2452,8 @@ public class AppOpsManager {
false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
false, // AUTO_REVOKE_MANAGED_BY_INSTALLER
true, // NO_ISOLATED_STORAGE
+ false, // PHONE_CALL_MICROPHONE
+ false, // PHONE_CALL_CAMERA
};
/**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f6b533434ac2..1b3e865945d1 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -187,6 +187,16 @@ class ContextImpl extends Context {
private static final String XATTR_INODE_CODE_CACHE = "user.inode_code_cache";
/**
+ * Special intent extra that critical system apps can use to hide the notification for a
+ * foreground service. This extra should be placed in the intent passed into {@link
+ * #startForegroundService(Intent)}.
+ *
+ * @hide
+ */
+ private static final String EXTRA_HIDDEN_FOREGROUND_SERVICE =
+ "android.intent.extra.HIDDEN_FOREGROUND_SERVICE";
+
+ /**
* Map from package name, to preference name, to cached preferences.
*/
@GuardedBy("ContextImpl.class")
@@ -1698,9 +1708,12 @@ class ContextImpl extends Context {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
+ final boolean hideForegroundNotification = requireForeground
+ && service.getBooleanExtra(EXTRA_HIDDEN_FOREGROUND_SERVICE, false);
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
+ hideForegroundNotification,
getOpPackageName(), getAttributionTag(), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f37ca61ba69f..0a47248e3b70 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -156,7 +156,8 @@ interface IActivityManager {
boolean refContentProvider(in IBinder connection, int stableDelta, int unstableDelta);
PendingIntent getRunningServiceControlPanel(in ComponentName service);
ComponentName startService(in IApplicationThread caller, in Intent service,
- in String resolvedType, boolean requireForeground, in String callingPackage,
+ in String resolvedType, boolean requireForeground,
+ boolean hideForegroundNotification, in String callingPackage,
in String callingFeatureId, int userId);
@UnsupportedAppUsage
int stopService(in IApplicationThread caller, in Intent service,
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index fb2120e5a35b..1aafe2cc6350 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -825,8 +825,8 @@ public class ResourcesManager {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#createApkAssetsSupplierNotLocked");
try {
- if (Thread.holdsLock(this)) {
- Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+ if (DEBUG && Thread.holdsLock(this)) {
+ Slog.w(TAG, "Calling thread " + Thread.currentThread().getName()
+ " is holding mLock", new Throwable());
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d2a774bcb168..c7b20b2a1dc3 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2674,52 +2674,6 @@ public final class BluetoothAdapter {
}
/**
- * Construct an encrypted, RFCOMM server socket.
- * Call #accept to retrieve connections to this socket.
- *
- * @return An RFCOMM BluetoothServerSocket
- * @throws IOException On error, for example Bluetooth not available, or insufficient
- * permissions.
- * @hide
- */
- public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port) throws IOException {
- BluetoothServerSocket socket =
- new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, true, port);
- int errno = socket.mSocket.bindListen();
- if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
- socket.setChannel(socket.mSocket.getPort());
- }
- if (errno < 0) {
- //TODO(BT): Throw the same exception error code
- // that the previous code was using.
- //socket.mSocket.throwErrnoNative(errno);
- throw new IOException("Error: " + errno);
- }
- return socket;
- }
-
- /**
- * Construct a SCO server socket.
- * Call #accept to retrieve connections to this socket.
- *
- * @return A SCO BluetoothServerSocket
- * @throws IOException On error, for example Bluetooth not available, or insufficient
- * permissions.
- * @hide
- */
- public static BluetoothServerSocket listenUsingScoOn() throws IOException {
- BluetoothServerSocket socket =
- new BluetoothServerSocket(BluetoothSocket.TYPE_SCO, false, false, -1);
- int errno = socket.mSocket.bindListen();
- if (errno < 0) {
- //TODO(BT): Throw the same exception error code
- // that the previous code was using.
- //socket.mSocket.throwErrnoNative(errno);
- }
- return socket;
- }
-
- /**
* Construct an encrypted, authenticated, L2CAP server socket.
* Call #accept to retrieve connections to this socket.
* <p>To auto assign a port without creating a SDP record use
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index bed7b26034e5..b7c3289b6e66 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2076,7 +2076,7 @@ public class PackageInstaller {
STAGED_SESSION_VERIFICATION_FAILED,
STAGED_SESSION_ACTIVATION_FAILED,
STAGED_SESSION_UNKNOWN,
- STAGED_SESSION_OTHER_ERROR})
+ STAGED_SESSION_CONFLICT})
@Retention(RetentionPolicy.SOURCE)
public @interface StagedSessionErrorCode{}
/**
@@ -2103,10 +2103,10 @@ public class PackageInstaller {
public static final int STAGED_SESSION_UNKNOWN = 3;
/**
- * Constant indicating that a known error occurred while processing this staged session, but
- * the error could not be matched to other categories.
+ * Constant indicating that the session was in conflict with another staged session and had
+ * to be sacrificed for resolution.
*/
- public static final int STAGED_SESSION_OTHER_ERROR = 4;
+ public static final int STAGED_SESSION_CONFLICT = 4;
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b0d449769be4..fecfd3c2dc7a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2954,7 +2954,7 @@ public abstract class BatteryStats implements Parcelable {
* enough current data to make a decision, or the battery is currently
* charging.
*
- * @param curTime The current elepsed realtime in microseconds.
+ * @param curTime The current elapsed realtime in microseconds.
*/
@UnsupportedAppUsage
public abstract long computeBatteryTimeRemaining(long curTime);
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 1eb3fc11df7b..df1f1b21eba3 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -68,7 +68,7 @@ public class GraphicsEnvironment {
private static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1";
private static final String PROPERTY_GFX_DRIVER_BUILD_TIME = "ro.gfx.driver_build_time";
private static final String METADATA_DRIVER_BUILD_TIME =
- "com.android.graphics.updatabledriver.build_time";
+ "com.android.graphics.driver.build_time";
private static final String METADATA_DEVELOPER_DRIVER_ENABLE =
"com.android.graphics.developerdriver.enable";
private static final String METADATA_INJECT_LAYERS_ENABLE =
@@ -878,9 +878,10 @@ public class GraphicsEnvironment {
throw new NullPointerException("apk's meta-data cannot be null");
}
- final String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME);
- if (driverBuildTime == null || driverBuildTime.isEmpty()) {
- Log.v(TAG, "com.android.graphics.updatabledriver.build_time is not set");
+ String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME);
+ if (driverBuildTime == null || driverBuildTime.length() <= 1) {
+ Log.v(TAG, "com.android.graphics.driver.build_time is not set");
+ driverBuildTime = "L0";
}
// driver_build_time in the meta-data is in "L<Unix epoch timestamp>" format. e.g. L123456.
// Long.parseLong will throw if the meta-data "driver_build_time" is not set properly.
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index ce6c0ffbc10b..a92d91bdefc5 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -18,6 +18,7 @@
package android.os;
import android.os.BatterySaverPolicyConfig;
+import android.os.ParcelDuration;
import android.os.PowerSaveState;
import android.os.WorkSource;
@@ -58,6 +59,9 @@ interface IPowerManager
boolean setAdaptivePowerSavePolicy(in BatterySaverPolicyConfig config);
boolean setAdaptivePowerSaveEnabled(boolean enabled);
int getPowerSaveModeTrigger();
+ void setBatteryDischargePrediction(in ParcelDuration timeRemaining, boolean isCustomized);
+ ParcelDuration getBatteryDischargePrediction();
+ boolean isBatteryDischargePredictionPersonalized();
boolean isDeviceIdleMode();
boolean isLightDeviceIdleMode();
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManager.java b/core/java/android/os/ParcelDuration.aidl
index 90187a298cf2..e1aa109f14ae 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManager.java
+++ b/core/java/android/os/ParcelDuration.aidl
@@ -14,31 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.onehanded;
+package android.os;
-/**
- * The base class of OneHandedManager
- */
-public interface OneHandedManager {
-
- /**
- * Set one handed enabled or disabled
- */
- default void setOneHandedEnabled(boolean enabled) {}
-
- /**
- * Set task stack changed to exit
- */
- default void setTaskChangeToExit(boolean enabled) {}
-
- /**
- * Exit one handed mode
- */
- default void stopOneHanded() {}
-
- /**
- * Trigger one handed mode
- */
- default void startOneHanded() {}
-
-}
+parcelable ParcelDuration cpp_header "android/ParcelDuration.h"; \ No newline at end of file
diff --git a/core/java/android/os/ParcelDuration.java b/core/java/android/os/ParcelDuration.java
new file mode 100644
index 000000000000..37cde3125cb5
--- /dev/null
+++ b/core/java/android/os/ParcelDuration.java
@@ -0,0 +1,103 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+
+import java.time.Duration;
+
+/**
+ * Parcelable version of {@link Duration} that can be used in binder calls.
+ *
+ * @hide
+ */
+public final class ParcelDuration implements Parcelable {
+
+ private final long mSeconds;
+ private final int mNanos;
+
+ /**
+ * Construct a Duration object using the given millisecond value.
+ *
+ * @hide
+ */
+ public ParcelDuration(long ms) {
+ this(Duration.ofMillis(ms));
+ }
+
+ /**
+ * Wrap a {@link Duration} instance.
+ *
+ * @param duration The {@link Duration} instance to wrap.
+ */
+ public ParcelDuration(@NonNull Duration duration) {
+ mSeconds = duration.getSeconds();
+ mNanos = duration.getNano();
+ }
+
+ private ParcelDuration(@NonNull Parcel parcel) {
+ mSeconds = parcel.readLong();
+ mNanos = parcel.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+ parcel.writeLong(mSeconds);
+ parcel.writeInt(mNanos);
+ }
+
+ /**
+ * Returns a {@link Duration} instance that's equivalent to this Duration's length.
+ *
+ * @return a {@link Duration} instance of identical length.
+ */
+ @NonNull
+ public Duration getDuration() {
+ return Duration.ofSeconds(mSeconds, mNanos);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return getDuration().toString();
+ }
+
+ /**
+ * Creator for Duration.
+ */
+ @NonNull
+ public static final Parcelable.Creator<ParcelDuration> CREATOR =
+ new Parcelable.Creator<ParcelDuration>() {
+
+ @Override
+ @NonNull
+ public ParcelDuration createFromParcel(@NonNull Parcel source) {
+ return new ParcelDuration(source);
+ }
+
+ @Override
+ @NonNull
+ public ParcelDuration[] newArray(int size) {
+ return new ParcelDuration[size];
+ }
+ };
+}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 309805f14eeb..ed38b3ff78e5 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -41,6 +41,7 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicLong;
@@ -1719,6 +1720,70 @@ public final class PowerManager {
}
/**
+ * Allows an app to tell the system how long it believes the battery will last and whether
+ * this estimate is customized based on historical device usage or on a generic configuration.
+ * These estimates will be displayed on system UI surfaces in place of the system computed
+ * value.
+ *
+ * Calling this requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+ *
+ * @param timeRemaining The time remaining as a {@link Duration}.
+ * @param isPersonalized true if personalized based on device usage history, false otherwise.
+ * @throws IllegalStateException if the device is powered or currently charging
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void setBatteryDischargePrediction(@NonNull Duration timeRemaining,
+ boolean isPersonalized) {
+ if (timeRemaining == null) {
+ throw new IllegalArgumentException("time remaining must not be null");
+ }
+ try {
+ mService.setBatteryDischargePrediction(new ParcelDuration(timeRemaining),
+ isPersonalized);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current battery life remaining estimate.
+ *
+ * @return The estimated battery life remaining as a {@link Duration}. Will be {@code null} if
+ * the device is powered, charging, or an error was encountered.
+ */
+ @Nullable
+ public Duration getBatteryDischargePrediction() {
+ try {
+ final ParcelDuration parcelDuration = mService.getBatteryDischargePrediction();
+ if (parcelDuration == null) {
+ return null;
+ }
+ return parcelDuration.getDuration();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether the current battery life remaining estimate is personalized based on device
+ * usage history or not. This value does not take a device's powered or charging state into
+ * account.
+ *
+ * @return A boolean indicating if the current discharge estimate is personalized based on
+ * historical device usage or not.
+ */
+ public boolean isBatteryDischargePredictionPersonalized() {
+ try {
+ return mService.isBatteryDischargePredictionPersonalized();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Get data about the battery saver mode for a specific service
* @param serviceType unique key for the service, one of {@link ServiceType}
* @return Battery saver state data.
@@ -2185,6 +2250,18 @@ public final class PowerManager {
}
/**
+ * Intent that is broadcast when the enhanced battery discharge prediction changes. The new
+ * value can be retrieved via {@link #getBatteryDischargePrediction()}.
+ * This broadcast is only sent to registered receivers.
+ *
+ * @hide
+ */
+ @TestApi
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED =
+ "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
+
+ /**
* Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
* This broadcast is only sent to registered receivers.
*/
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 50cc764dd536..58c8efa3a972 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -102,6 +102,8 @@ public final class Trace {
/** @hide */
public static final long TRACE_TAG_RRO = 1L << 26;
/** @hide */
+ public static final long TRACE_TAG_SYSPROP = 1L << 27;
+ /** @hide */
public static final long TRACE_TAG_APEX_MANAGER = 1L << 18;
private static final long TRACE_TAG_NOT_READY = 1L << 63;
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index b45a1eba1e2f..859b70382328 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -197,6 +197,13 @@ public final class DeviceConfig {
"intelligence_content_suggestions";
/**
+ * Namespace for JobScheduler configurations.
+ * @hide
+ */
+ @TestApi
+ public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
+
+ /**
* Namespace for all media native related features.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6903a995bf7b..c302def19298 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -65,6 +65,7 @@ import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.IBinder;
import android.os.LocaleList;
+import android.os.PowerManager;
import android.os.PowerManager.AutoPowerSaveModeTriggers;
import android.os.Process;
import android.os.RemoteCallback;
@@ -11844,36 +11845,6 @@ public final class Settings {
public static final String ALARM_MANAGER_CONSTANTS = "alarm_manager_constants";
/**
- * Job scheduler specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * "min_ready_jobs_count=2,moderate_use_factor=.5"
- *
- * The following keys are supported:
- *
- * <pre>
- * min_idle_count (int)
- * min_charging_count (int)
- * min_connectivity_count (int)
- * min_content_count (int)
- * min_ready_jobs_count (int)
- * heavy_use_factor (float)
- * moderate_use_factor (float)
- * fg_job_count (int)
- * bg_normal_job_count (int)
- * bg_moderate_job_count (int)
- * bg_low_job_count (int)
- * bg_critical_job_count (int)
- * </pre>
- *
- * <p>
- * Type: string
- * @hide
- * @see com.android.server.job.JobSchedulerService.Constants
- */
- public static final String JOB_SCHEDULER_CONSTANTS = "job_scheduler_constants";
-
- /**
* Job scheduler QuotaController specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
@@ -12545,18 +12516,23 @@ public final class Settings {
* millis. See {@link #BATTERY_ESTIMATES_LAST_UPDATE_TIME} for the last time this value
* was updated.
*
+ * @deprecated Use {@link PowerManager#getBatteryDischargePrediction()} instead.
* @hide
*/
+ @Deprecated
public static final String TIME_REMAINING_ESTIMATE_MILLIS =
"time_remaining_estimate_millis";
/**
- * A boolean indicating whether {@link #TIME_REMAINING_ESTIMATE_MILLIS} is based customized
- * to the devices usage or using global models. See
+ * A boolean indicating whether {@link #TIME_REMAINING_ESTIMATE_MILLIS} is customized
+ * to the device's usage or using global models. See
* {@link #BATTERY_ESTIMATES_LAST_UPDATE_TIME} for the last time this value was updated.
*
+ * @deprecated Use {@link PowerManager#isBatteryDischargePredictionPersonalized()} instead.
+ *
* @hide
*/
+ @Deprecated
public static final String TIME_REMAINING_ESTIMATE_BASED_ON_USAGE =
"time_remaining_estimate_based_on_usage";
@@ -12565,8 +12541,10 @@ public final class Settings {
* average based on historical drain rates. See {@link #BATTERY_ESTIMATES_LAST_UPDATE_TIME}
* for the last time this value was updated.
*
+ * @deprecated Use {@link PowerManager#getHistoricalDischargeTime()} instead.
* @hide
*/
+ @Deprecated
public static final String AVERAGE_TIME_TO_DISCHARGE = "average_time_to_discharge";
/**
@@ -12575,7 +12553,9 @@ public final class Settings {
* and {@link #AVERAGE_TIME_TO_DISCHARGE} were last updated.
*
* @hide
+ * @deprecated No longer needed due to {@link PowerManager#getBatteryDischargePrediction}.
*/
+ @Deprecated
public static final String BATTERY_ESTIMATES_LAST_UPDATE_TIME =
"battery_estimates_last_update_time";
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 678f43daaa38..04a4ca47d305 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -137,7 +137,7 @@ import android.view.autofill.AutofillValue;
* <p>The service can provide an extra degree of security by requiring the user to authenticate
* before an app can be autofilled. The authentication is typically required in 2 scenarios:
* <ul>
- * <li>To unlock the user data (for example, using a master password or fingerprint
+ * <li>To unlock the user data (for example, using a main password or fingerprint
* authentication) - see
* {@link FillResponse.Builder#setAuthentication(AutofillId[], android.content.IntentSender, android.widget.RemoteViews)}.
* <li>To unlock a specific dataset (for example, by providing a CVC for a credit card) - see
@@ -363,9 +363,9 @@ import android.view.autofill.AutofillValue;
* {@code login.some_bank.com} credentials to the {@code my_financial_app}; if the user agrees,
* then the service returns an unlocked dataset with the {@code some_bank.com} credentials.
*
- * <p><b>Note:</b> The autofill service could also whitelist well-known browser apps and skip the
- * verifications above, as long as the service can verify the authenticity of the browser app by
- * checking its signing certificate.
+ * <p><b>Note:</b> The autofill service could also add well-known browser apps into an allowlist and
+ * skip the verifications above, as long as the service can verify the authenticity of the browser
+ * app by checking its signing certificate.
*
* <a name="MultipleStepsSave"></a>
* <h3>Saving when data is split in multiple screens</h3>
@@ -507,9 +507,9 @@ import android.view.autofill.AutofillValue;
* services and fill data. This mode needs to be explicitly requested for a given package up
* to a specified max version code allowing clean migration path when the target app begins to
* support autofill natively. Note that enabling compatibility may degrade performance for the
- * target package and should be used with caution. The platform supports whitelisting which packages
- * can be targeted in compatibility mode to ensure this mode is used only when needed and as long
- * as needed.
+ * target package and should be used with caution. The platform supports creating an allowlist for
+ * including which packages can be targeted in compatibility mode to ensure this mode is used only
+ * when needed and as long as needed.
*
* <p>You can request compatibility mode for packages of interest in the meta-data resource
* associated with your service. Below is a sample service declaration:
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index e341845277d4..1ef701f732ff 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -70,11 +70,8 @@ public final class InputWindowHandle {
// Window is visible.
public boolean visible;
- // Window can receive keys.
- public boolean canReceiveKeys;
-
- // Window has focus.
- public boolean hasFocus;
+ // Window can be focused.
+ public boolean focusable;
// Window has wallpaper. (window is the current wallpaper target)
public boolean hasWallpaper;
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 7f45c044408a..403ac3ab29c0 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -629,7 +629,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
mHost.notifyInsetsChanged();
}
- if (!mState.equals(state, true /* excludingCaptionInsets */,
+ if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */,
true /* excludeInvisibleIme */)) {
if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState);
updateRequestedState();
@@ -1138,15 +1138,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (invokeCallback) {
control.cancel();
}
+ boolean stateChanged = false;
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
RunningAnimation runningAnimation = mRunningAnimations.get(i);
if (runningAnimation.runner == control) {
mRunningAnimations.remove(i);
ArraySet<Integer> types = toInternalType(control.getTypes());
for (int j = types.size() - 1; j >= 0; j--) {
- if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) {
- mHost.notifyInsetsChanged();
- }
+ stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished();
}
if (invokeCallback && runningAnimation.startDispatched) {
dispatchAnimationEnd(runningAnimation.runner.getAnimation());
@@ -1154,6 +1153,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
break;
}
}
+ if (stateChanged) {
+ mHost.notifyInsetsChanged();
+ updateRequestedState();
+ }
}
private void applyLocalVisibilityOverride() {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 6b0b509932a8..593b37af26ad 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -60,6 +60,8 @@ import java.util.StringJoiner;
*/
public class InsetsState implements Parcelable {
+ public static final InsetsState EMPTY = new InsetsState();
+
/**
* Internal representation of inset source types. This is different from the public API in
* {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 859e9a43f7c2..65cc2f8bcd5a 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -19,6 +19,7 @@ package android.view;
import android.animation.Animator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
+import android.annotation.FloatRange;
import android.graphics.RenderNode;
import java.util.ArrayList;
@@ -725,7 +726,7 @@ public class ViewPropertyAnimator {
* @see View#setAlpha(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator alpha(float value) {
+ public ViewPropertyAnimator alpha(@FloatRange(from = 0.0f, to = 1.0f) float value) {
animateProperty(ALPHA, value);
return this;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3f02d701f71f..5d20381242bd 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -8890,6 +8890,9 @@ public final class ViewRootImpl implements ViewParent,
* @param targets the search queue for targets
*/
private void collectRootScrollCaptureTargets(Queue<ScrollCaptureTarget> targets) {
+ if (mRootScrollCaptureCallbacks == null) {
+ return;
+ }
for (ScrollCaptureCallback cb : mRootScrollCaptureCallbacks) {
// Add to the list for consideration
Point offset = new Point(mView.getLeft(), mView.getTop());
diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java
index 7e7b987f72f3..b04105ab1e75 100644
--- a/core/java/android/webkit/PacProcessor.java
+++ b/core/java/android/webkit/PacProcessor.java
@@ -32,6 +32,10 @@ public interface PacProcessor {
/**
* Returns the default PacProcessor instance.
*
+ * <p> There can only be one default {@link PacProcessor} instance.
+ * This method will create a new instance if one did not already exist, or
+ * if the previous instance was released with {@link #releasePacProcessor}.
+ *
* @return the default PacProcessor instance.
*/
@NonNull
@@ -43,14 +47,21 @@ public interface PacProcessor {
* Returns PacProcessor instance associated with the {@link Network}.
* The host resolution is done on this {@link Network}.
*
- * @param networkHandle a handle representing {@link Network} handle.
- * @return PacProcessor instance for the specified network.
- * @see Network#getNetworkHandle
- * @see Network#fromNetworkHandle
+ * <p> There can only be one {@link PacProcessor} instance at a time for each {@link Network}.
+ * This method will create a new instance if one did not already exist, or
+ * if the previous instance was released with {@link #releasePacProcessor}.
+ *
+ * <p> The {@link PacProcessor} instance needs to be released manually with
+ * {@link #releasePacProcessor} when the associated {@link Network} goes away.
+ *
+ * @param network a {@link Network} which this {@link PacProcessor}
+ * will use for host/address resolution.
+ * If {@code null} this method is equivalent to {@link #getInstance}.
+ * @return {@link PacProcessor} instance for the specified network.
*/
@NonNull
- static PacProcessor getInstanceForNetwork(long networkHandle) {
- return WebViewFactory.getProvider().getPacProcessorForNetwork(networkHandle);
+ static PacProcessor getInstanceForNetwork(@Nullable Network network) {
+ return WebViewFactory.getProvider().getPacProcessorForNetwork(network);
}
/**
@@ -73,19 +84,22 @@ public interface PacProcessor {
/**
* Stops support for this {@link PacProcessor} and release its resources.
* No methods of this class must be called after calling this method.
+ *
+ * <p> Released instances will not be reused; a subsequent call to
+ * {@link #getInstance} and {@link #getInstanceForNetwork}
+ * for the same network will create a new instance.
*/
default void releasePacProcessor() {
throw new UnsupportedOperationException("Not implemented");
}
/**
- * Returns a network handle associated with this {@link PacProcessor}.
+ * Returns a {@link Network} associated with this {@link PacProcessor}.
*
- * @return a network handle or 0 if a network is unspecified.
- * @see Network#getNetworkHandle
- * @see Network#fromNetworkHandle
+ * @return an associated {@link Network} or {@code null} if a network is unspecified.
*/
- default long getNetworkHandle() {
+ @Nullable
+ default Network getNetwork() {
throw new UnsupportedOperationException("Not implemented");
}
}
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index f1863e319689..ce999cd5e235 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -17,6 +17,7 @@
package android.webkit;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
@@ -188,13 +189,13 @@ public interface WebViewFactoryProvider {
* Returns PacProcessor instance associated with the {@link Network}.
* The host resolution is done on this {@link Network}.
*
- * @param networkHandle a network handle representing the {@link Network}.
+ * @param network a {@link Network} which needs to be associated
+ * with the returned {@link PacProcessor}.
+ * If {@code null} the method returns default {@link PacProcessor}.
* @return the {@link PacProcessor} instance associated with {@link Network}.
- * @see Network#getNetworkHandle
- * @see Network#fromNetworkHandle
*/
@NonNull
- default PacProcessor getPacProcessorForNetwork(long networkHandle) {
+ default PacProcessor getPacProcessorForNetwork(@Nullable Network network) {
throw new UnsupportedOperationException("Not implemented");
}
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index 1c03b2fdf906..92fa80e40caf 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -25,11 +25,9 @@ import android.window.WindowContainerTransaction;
interface ITaskOrganizerController {
/**
- * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
- * If there was already a TaskOrganizer for this windowing mode it will be evicted
- * and receive taskVanished callbacks in the process.
+ * Register a TaskOrganizer to manage all the tasks with supported windowing modes.
*/
- void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode);
+ void registerTaskOrganizer(ITaskOrganizer organizer);
/**
* Unregisters a previously registered task organizer.
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 502680de9bcf..7ec4f99ce959 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -36,14 +36,12 @@ import java.util.List;
public class TaskOrganizer extends WindowOrganizer {
/**
- * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
- * If there was already a TaskOrganizer for this windowing mode it will be evicted
- * and receive taskVanished callbacks in the process.
+ * Register a TaskOrganizer to manage tasks as they enter a supported windowing mode.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
- public final void registerOrganizer(int windowingMode) {
+ public final void registerOrganizer() {
try {
- getController().registerTaskOrganizer(mInterface, windowingMode);
+ getController().registerTaskOrganizer(mInterface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/window/TaskOrganizerTaskEmbedder.java b/core/java/android/window/TaskOrganizerTaskEmbedder.java
index 1b87521f3a96..46c72f88e14b 100644
--- a/core/java/android/window/TaskOrganizerTaskEmbedder.java
+++ b/core/java/android/window/TaskOrganizerTaskEmbedder.java
@@ -73,7 +73,7 @@ public class TaskOrganizerTaskEmbedder extends TaskEmbedder {
// TODO(wm-shell): This currently prevents other organizers from controlling MULT_WINDOW
// windowing mode tasks. Plan is to migrate this to a wm-shell front-end when that
// infrastructure is ready.
- mTaskOrganizer.registerOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+ // mTaskOrganizer.registerOrganizer();
mTaskOrganizer.setInterceptBackPressedOnTaskRoot(true);
return super.onInitialize();
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 053b06f3d407..2f8c45770eb5 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -347,7 +347,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
super(context);
mLayoutInflater = LayoutInflater.from(context);
mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(),
- DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0;
+ DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
}
/**
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index c82a6564c1d7..937b9426476a 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -200,6 +200,20 @@ public class Preconditions {
}
/**
+ * Ensures the truth of an expression involving whether the calling identity is authorized to
+ * call the calling method.
+ *
+ * @param expression a boolean expression
+ * @param message the message of the security exception to be thrown
+ * @throws SecurityException if {@code expression} is false
+ */
+ public static void checkSecurity(final boolean expression, final String message) {
+ if (!expression) {
+ throw new SecurityException(message);
+ }
+ }
+
+ /**
* Ensures the truth of an expression involving whether the calling user is authorized to
* call the calling method.
*
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index d5d635de81d8..654b46164dcf 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -49,7 +49,8 @@ interface ILockSettings {
in ICheckCredentialProgressCallback progressCallback);
VerifyCredentialResponse verifyCredential(in LockscreenCredential credential, int userId, int flags);
VerifyCredentialResponse verifyTiedProfileChallenge(in LockscreenCredential credential, int userId, int flags);
- VerifyCredentialResponse verifyGatekeeperPassword(in byte[] gatekeeperPassword, long challenge, int userId);
+ VerifyCredentialResponse verifyGatekeeperPasswordHandle(long gatekeeperPasswordHandle, long challenge, int userId);
+ void removeGatekeeperPasswordHandle(long gatekeeperPasswordHandle);
boolean checkVoldPassword(int userId);
int getCredentialType(int userId);
byte[] getHashFactor(in LockscreenCredential currentCredential, int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index f7370d6a22f9..960c11f4c55c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -130,14 +130,15 @@ public class LockPatternUtils {
public @interface CredentialType {}
/**
- * Flag provided to {@link #verifyCredential(LockscreenCredential, long, int, int)} . If set,
- * the method will return the Gatekeeper Password in the {@link VerifyCredentialResponse}.
+ * Flag provided to {@link #verifyCredential(LockscreenCredential, int, int)} . If set, the
+ * method will return a handle to the Gatekeeper Password in the
+ * {@link VerifyCredentialResponse}.
*/
- public static final int VERIFY_FLAG_RETURN_GK_PW = 1 << 0;
+ public static final int VERIFY_FLAG_REQUEST_GK_PW_HANDLE = 1 << 0;
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
- VERIFY_FLAG_RETURN_GK_PW
+ VERIFY_FLAG_REQUEST_GK_PW_HANDLE
})
public @interface VerifyFlag {}
@@ -409,16 +410,16 @@ public class LockPatternUtils {
}
/**
- * With the Gatekeeper Password returned via {@link #verifyCredential(LockscreenCredential,
- * int, int)}, request Gatekeeper to create a HardwareAuthToken wrapping the given
- * challenge.
+ * With the Gatekeeper Password Handle returned via {@link #verifyCredential(
+ * LockscreenCredential, int, int)}, request Gatekeeper to create a HardwareAuthToken wrapping
+ * the given challenge.
*/
@NonNull
- public VerifyCredentialResponse verifyGatekeeperPassword(@NonNull byte[] gatekeeperPassword,
+ public VerifyCredentialResponse verifyGatekeeperPasswordHandle(long gatekeeperPasswordHandle,
long challenge, int userId) {
try {
- final VerifyCredentialResponse response = getLockSettings().verifyGatekeeperPassword(
- gatekeeperPassword, challenge, userId);
+ final VerifyCredentialResponse response = getLockSettings()
+ .verifyGatekeeperPasswordHandle(gatekeeperPasswordHandle, challenge, userId);
if (response == null) {
return VerifyCredentialResponse.ERROR;
}
@@ -429,6 +430,14 @@ public class LockPatternUtils {
}
}
+ public void removeGatekeeperPasswordHandle(long gatekeeperPasswordHandle) {
+ try {
+ getLockSettings().removeGatekeeperPasswordHandle(gatekeeperPasswordHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to remove gatekeeper password handle", e);
+ }
+ }
+
/**
* Check to see if a credential matches the saved one.
*
diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
index e09eb4228219..ab146341cbaa 100644
--- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java
+++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
@@ -49,7 +49,7 @@ public final class VerifyCredentialResponse implements Parcelable {
private final @ResponseCode int mResponseCode;
private final int mTimeout;
@Nullable private final byte[] mGatekeeperHAT;
- @Nullable private final byte[] mGatekeeperPw;
+ private final long mGatekeeperPasswordHandle;
public static final Parcelable.Creator<VerifyCredentialResponse> CREATOR
= new Parcelable.Creator<VerifyCredentialResponse>() {
@@ -58,10 +58,10 @@ public final class VerifyCredentialResponse implements Parcelable {
final @ResponseCode int responseCode = source.readInt();
final int timeout = source.readInt();
final byte[] gatekeeperHAT = source.createByteArray();
- final byte[] gatekeeperPassword = source.createByteArray();
+ long gatekeeperPasswordHandle = source.readLong();
return new VerifyCredentialResponse(responseCode, timeout, gatekeeperHAT,
- gatekeeperPassword);
+ gatekeeperPasswordHandle);
}
@Override
@@ -72,7 +72,7 @@ public final class VerifyCredentialResponse implements Parcelable {
public static class Builder {
@Nullable private byte[] mGatekeeperHAT;
- @Nullable private byte[] mGatekeeperPassword;
+ private long mGatekeeperPasswordHandle;
/**
* @param gatekeeperHAT Gatekeeper HardwareAuthToken, minted upon successful authentication.
@@ -82,8 +82,8 @@ public final class VerifyCredentialResponse implements Parcelable {
return this;
}
- public Builder setGatekeeperPassword(byte[] gatekeeperPassword) {
- mGatekeeperPassword = gatekeeperPassword;
+ public Builder setGatekeeperPasswordHandle(long gatekeeperPasswordHandle) {
+ mGatekeeperPasswordHandle = gatekeeperPasswordHandle;
return this;
}
@@ -96,7 +96,7 @@ public final class VerifyCredentialResponse implements Parcelable {
return new VerifyCredentialResponse(RESPONSE_OK,
0 /* timeout */,
mGatekeeperHAT,
- mGatekeeperPassword);
+ mGatekeeperPasswordHandle);
}
}
@@ -110,7 +110,7 @@ public final class VerifyCredentialResponse implements Parcelable {
return new VerifyCredentialResponse(RESPONSE_RETRY,
timeout,
null /* gatekeeperHAT */,
- null /* gatekeeperPassword */);
+ 0L /* gatekeeperPasswordHandle */);
}
/**
@@ -121,20 +121,20 @@ public final class VerifyCredentialResponse implements Parcelable {
return new VerifyCredentialResponse(RESPONSE_ERROR,
0 /* timeout */,
null /* gatekeeperHAT */,
- null /* gatekeeperPassword */);
+ 0L /* gatekeeperPasswordHandle */);
}
private VerifyCredentialResponse(@ResponseCode int responseCode, int timeout,
- @Nullable byte[] gatekeeperHAT, @Nullable byte[] gatekeeperPassword) {
+ @Nullable byte[] gatekeeperHAT, long gatekeeperPasswordHandle) {
mResponseCode = responseCode;
mTimeout = timeout;
mGatekeeperHAT = gatekeeperHAT;
- mGatekeeperPw = gatekeeperPassword;
+ mGatekeeperPasswordHandle = gatekeeperPasswordHandle;
}
public VerifyCredentialResponse stripPayload() {
return new VerifyCredentialResponse(mResponseCode, mTimeout,
- null /* gatekeeperHAT */, null /* gatekeeperPassword */);
+ null /* gatekeeperHAT */, 0L /* gatekeeperPasswordHandle */);
}
@Override
@@ -142,7 +142,7 @@ public final class VerifyCredentialResponse implements Parcelable {
dest.writeInt(mResponseCode);
dest.writeInt(mTimeout);
dest.writeByteArray(mGatekeeperHAT);
- dest.writeByteArray(mGatekeeperPw);
+ dest.writeLong(mGatekeeperPasswordHandle);
}
@Override
@@ -155,9 +155,12 @@ public final class VerifyCredentialResponse implements Parcelable {
return mGatekeeperHAT;
}
- @Nullable
- public byte[] getGatekeeperPw() {
- return mGatekeeperPw;
+ public long getGatekeeperPasswordHandle() {
+ return mGatekeeperPasswordHandle;
+ }
+
+ public boolean containsGatekeeperPasswordHandle() {
+ return mGatekeeperPasswordHandle != 0L;
}
public int getTimeout() {
@@ -176,7 +179,7 @@ public final class VerifyCredentialResponse implements Parcelable {
public String toString() {
return "Response: " + mResponseCode
+ ", GK HAT: " + (mGatekeeperHAT != null)
- + ", GK PW: " + (mGatekeeperPw != null);
+ + ", GK PW: " + (mGatekeeperPasswordHandle != 0L);
}
public static VerifyCredentialResponse fromGateKeeperResponse(
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index ecdba3fcb023..a0638207a841 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -56,8 +56,7 @@ static struct {
jfieldID scaleFactor;
jfieldID touchableRegion;
jfieldID visible;
- jfieldID canReceiveKeys;
- jfieldID hasFocus;
+ jfieldID focusable;
jfieldID hasWallpaper;
jfieldID paused;
jfieldID trustedOverlay;
@@ -145,10 +144,7 @@ bool NativeInputWindowHandle::updateInfo() {
mInfo.visible = env->GetBooleanField(obj,
gInputWindowHandleClassInfo.visible);
- mInfo.canReceiveKeys = env->GetBooleanField(obj,
- gInputWindowHandleClassInfo.canReceiveKeys);
- mInfo.hasFocus = env->GetBooleanField(obj,
- gInputWindowHandleClassInfo.hasFocus);
+ mInfo.focusable = env->GetBooleanField(obj, gInputWindowHandleClassInfo.focusable);
mInfo.hasWallpaper = env->GetBooleanField(obj,
gInputWindowHandleClassInfo.hasWallpaper);
mInfo.paused = env->GetBooleanField(obj,
@@ -320,11 +316,7 @@ int register_android_view_InputWindowHandle(JNIEnv* env) {
GET_FIELD_ID(gInputWindowHandleClassInfo.visible, clazz,
"visible", "Z");
- GET_FIELD_ID(gInputWindowHandleClassInfo.canReceiveKeys, clazz,
- "canReceiveKeys", "Z");
-
- GET_FIELD_ID(gInputWindowHandleClassInfo.hasFocus, clazz,
- "hasFocus", "Z");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.focusable, clazz, "focusable", "Z");
GET_FIELD_ID(gInputWindowHandleClassInfo.hasWallpaper, clazz,
"hasWallpaper", "Z");
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index f8f841c6fd26..3af55fe810fc 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -269,22 +269,9 @@ jobject JHwRemoteBinder::NewObject(
return obj;
}
-JHwRemoteBinder::JHwRemoteBinder(
- JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder)
- : mBinder(binder) {
- mDeathRecipientList = new HwBinderDeathRecipientList();
- jclass clazz = env->GetObjectClass(thiz);
- CHECK(clazz != NULL);
-
- mObject = env->NewWeakGlobalRef(thiz);
-}
-
-JHwRemoteBinder::~JHwRemoteBinder() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
-
- env->DeleteWeakGlobalRef(mObject);
- mObject = NULL;
-}
+JHwRemoteBinder::JHwRemoteBinder(JNIEnv* env, jobject /* thiz */,
+ const sp<hardware::IBinder>& binder)
+ : mBinder(binder), mDeathRecipientList(new HwBinderDeathRecipientList()) {}
sp<hardware::IBinder> JHwRemoteBinder::getBinder() const {
return mBinder;
diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h
index 4b5a4c8c837c..7eb81f3c54e1 100644
--- a/core/jni/android_os_HwRemoteBinder.h
+++ b/core/jni/android_os_HwRemoteBinder.h
@@ -36,9 +36,13 @@ class HwBinderDeathRecipientList : public RefBase {
std::vector<sp<HwBinderDeathRecipient>> mList;
Mutex mLock;
+protected:
+ ~HwBinderDeathRecipientList() override;
+
public:
- HwBinderDeathRecipientList();
- ~HwBinderDeathRecipientList();
+ explicit HwBinderDeathRecipientList();
+
+ DISALLOW_COPY_AND_ASSIGN(HwBinderDeathRecipientList);
void add(const sp<HwBinderDeathRecipient>& recipient);
void remove(const sp<HwBinderDeathRecipient>& recipient);
@@ -66,12 +70,7 @@ struct JHwRemoteBinder : public RefBase {
void setBinder(const sp<hardware::IBinder> &binder);
sp<HwBinderDeathRecipientList> getDeathRecipientList() const;
-protected:
- virtual ~JHwRemoteBinder();
-
private:
- jobject mObject;
-
sp<hardware::IBinder> mBinder;
sp<HwBinderDeathRecipientList> mDeathRecipientList;
DISALLOW_COPY_AND_ASSIGN(JHwRemoteBinder);
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index d5619ca96fbd..9fccdaf9684f 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -507,7 +507,7 @@ message GlobalSettingsProto {
}
optional IntentFirewall intent_firewall = 65;
- optional SettingProto job_scheduler_constants = 66 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ reserved 66; // job_scheduler_constants
optional SettingProto job_scheduler_quota_controller_constants = 149 [ (android.privacy).dest = DEST_AUTOMATIC ];
reserved 150; // job_scheduler_time_controller_constants
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index 0455d58f498b..a2f2c46cba6a 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -174,6 +174,16 @@ message PowerManagerServiceDumpProto {
optional BatterySaverStateMachineProto battery_saver_state_machine = 50;
// Attentive timeout in ms. The timeout is disabled if it is set to -1.
optional sint32 attentive_timeout_ms = 51;
+ // The time (in the elapsed realtime timebase) at which the battery level will reach 0%. This
+ // is provided as an enhanced estimate and only valid if
+ // last_enhanced_discharge_time_updated_elapsed is greater than 0.
+ optional int64 enhanced_discharge_time_elapsed = 52;
+ // Timestamp (in the elapsed realtime timebase) of last update to enhanced battery estimate
+ // data.
+ optional int64 last_enhanced_discharge_time_updated_elapsed = 53;
+ // Whether or not the current enhanced discharge prediction is personalized based on device
+ // usage or not.
+ optional bool is_enhanced_discharge_prediction_personalized = 54;
}
// A com.android.server.power.PowerManagerService.SuspendBlockerImpl object.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 32c1e4a1411c..57c1fcf7bfb4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -102,6 +102,7 @@
<protected-broadcast android:name="android.os.action.POWER_SAVE_WHITELIST_CHANGED" />
<protected-broadcast android:name="android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED" />
<protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL" />
+ <protected-broadcast android:name="android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED" />
<!-- @deprecated This is rarely used and will be phased out soon. -->
<protected-broadcast android:name="android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED" />
@@ -5040,6 +5041,10 @@
<permission android:name="android.permission.RESET_APP_ERRORS"
android:protectionLevel="signature" />
+ <!-- @hide Allows an application to create/destroy input consumer. -->
+ <permission android:name="android.permission.INPUT_CONSUMER"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index af02b7bdbd90..de128ad6d78e 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -746,6 +746,20 @@ public class InsetsControllerTest {
mController.onControlsChanged(createSingletonControl(ITYPE_IME));
assertEquals(newState.getSource(ITYPE_IME),
mTestHost.getModifiedState().peekSource(ITYPE_IME));
+
+ // The modified frames cannot be updated if there is an animation.
+ mController.onControlsChanged(createSingletonControl(ITYPE_NAVIGATION_BAR));
+ mController.hide(navigationBars());
+ newState = new InsetsState(mController.getState(), true /* copySource */);
+ newState.getSource(ITYPE_NAVIGATION_BAR).getFrame().top--;
+ mController.onStateChanged(newState);
+ assertNotEquals(newState.getSource(ITYPE_NAVIGATION_BAR),
+ mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR));
+
+ // The modified frames can be updated while the animation is done.
+ mController.cancelExistingAnimations();
+ assertEquals(newState.getSource(ITYPE_NAVIGATION_BAR),
+ mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR));
});
}
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index d0e688d3efc1..e122e0026aac 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -144,13 +144,6 @@ prebuilt_etc {
}
prebuilt_etc {
- name: "allowed_privapp_com.android.car.floatingcardslauncher",
- sub_dir: "permissions",
- src: "com.android.car.floatingcardslauncher.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
name: "allowed_privapp_com.android.car.ui.paintbooth",
sub_dir: "permissions",
src: "com.android.car.ui.paintbooth.xml",
diff --git a/data/etc/car/com.android.car.floatingcardslauncher.xml b/data/etc/car/com.android.car.floatingcardslauncher.xml
deleted file mode 100644
index 2755fee4eb55..000000000000
--- a/data/etc/car/com.android.car.floatingcardslauncher.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<permissions>
- <privapp-permissions package="com.android.car.floatingcardslauncher">
- <permission name="android.permission.ACTIVITY_EMBEDDING"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- </privapp-permissions>
-</permissions>
diff --git a/graphics/java/android/graphics/BlurShader.java b/graphics/java/android/graphics/BlurShader.java
new file mode 100644
index 000000000000..779a89051060
--- /dev/null
+++ b/graphics/java/android/graphics/BlurShader.java
@@ -0,0 +1,65 @@
+/*
+ * 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.graphics;
+
+import android.annotation.Nullable;
+
+/**
+ * A subclass of shader that blurs input from another {@link android.graphics.Shader} instance
+ * or all the drawing commands with the {@link android.graphics.Paint} that this shader is
+ * attached to.
+ */
+public final class BlurShader extends Shader {
+
+ private final float mRadiusX;
+ private final float mRadiusY;
+ private final Shader mInputShader;
+
+ private long mNativeInputShader = 0;
+
+ /**
+ * Create a {@link BlurShader} that blurs the contents of the optional input shader
+ * with the specified radius along the x and y axis. If no input shader is provided
+ * then all drawing commands issued with a {@link android.graphics.Paint} that this
+ * shader is installed in will be blurred
+ * @param radiusX Radius of blur along the X axis
+ * @param radiusY Radius of blur along the Y axis
+ * @param inputShader Input shader that provides the content to be blurred
+ */
+ public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader) {
+ mRadiusX = radiusX;
+ mRadiusY = radiusY;
+ mInputShader = inputShader;
+ }
+
+ /** @hide **/
+ @Override
+ protected long createNativeInstance(long nativeMatrix) {
+ mNativeInputShader = mInputShader != null ? mInputShader.getNativeInstance() : 0;
+ return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader);
+ }
+
+ /** @hide **/
+ @Override
+ protected boolean shouldDiscardNativeInstance() {
+ long currentNativeInstance = mInputShader != null ? mInputShader.getNativeInstance() : 0;
+ return mNativeInputShader != currentNativeInstance;
+ }
+
+ private static native long nativeCreate(long nativeMatrix, float radiusX, float radiusY,
+ long inputShader);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
new file mode 100644
index 000000000000..126374829a18
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.window.TaskOrganizer;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Unified task organizer for all components in the shell.
+ */
+public class ShellTaskOrganizer extends TaskOrganizer {
+
+ private static final String TAG = "ShellTaskOrganizer";
+
+ /**
+ * Callbacks for when the tasks change in the system.
+ */
+ public interface TaskListener {
+ default void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {}
+ default void onTaskInfoChanged(RunningTaskInfo taskInfo) {}
+ default void onTaskVanished(RunningTaskInfo taskInfo) {}
+ default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
+ }
+
+ private final SparseArray<ArrayList<TaskListener>> mListenersByWindowingMode =
+ new SparseArray<>();
+
+ // Keeps track of all the tasks reported to this organizer (changes in windowing mode will
+ // require us to report to both old and new listeners)
+ private final SparseArray<Pair<RunningTaskInfo, SurfaceControl>> mTasks = new SparseArray<>();
+
+ /**
+ * Adds a listener for tasks in a specific windowing mode.
+ */
+ public void addListener(TaskListener listener, int... windowingModes) {
+ for (int winMode : windowingModes) {
+ ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(winMode);
+ if (listeners == null) {
+ listeners = new ArrayList<>();
+ mListenersByWindowingMode.put(winMode, listeners);
+ }
+ if (listeners.contains(listener)) {
+ Log.w(TAG, "Listener already exists");
+ return;
+ }
+ listeners.add(listener);
+
+ // Notify the listener of all existing tasks in that windowing mode
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ Pair<RunningTaskInfo, SurfaceControl> data = mTasks.valueAt(i);
+ int taskWinMode = data.first.configuration.windowConfiguration.getWindowingMode();
+ if (taskWinMode == winMode) {
+ listener.onTaskAppeared(data.first, data.second);
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a registered listener.
+ */
+ public void removeListener(TaskListener listener) {
+ for (int i = 0; i < mListenersByWindowingMode.size(); i++) {
+ mListenersByWindowingMode.valueAt(i).remove(listener);
+ }
+ }
+
+ @Override
+ public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+ mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, leash));
+ ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(
+ getWindowingMode(taskInfo));
+ if (listeners != null) {
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onTaskAppeared(taskInfo, leash);
+ }
+ }
+ }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId);
+ int winMode = getWindowingMode(taskInfo);
+ int prevWinMode = getWindowingMode(data.first);
+ if (prevWinMode != -1 && prevWinMode != winMode) {
+ // TODO: We currently send vanished/appeared as the task moves between win modes, but
+ // we should consider adding a different mode-changed callback
+ ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(prevWinMode);
+ if (listeners != null) {
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onTaskVanished(taskInfo);
+ }
+ }
+ listeners = mListenersByWindowingMode.get(winMode);
+ if (listeners != null) {
+ SurfaceControl leash = data.second;
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onTaskAppeared(taskInfo, leash);
+ }
+ }
+ } else {
+ ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(winMode);
+ if (listeners != null) {
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onTaskInfoChanged(taskInfo);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
+ ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(
+ getWindowingMode(taskInfo));
+ if (listeners != null) {
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onBackPressedOnTaskRoot(taskInfo);
+ }
+ }
+ }
+
+ @Override
+ public void onTaskVanished(RunningTaskInfo taskInfo) {
+ int prevWinMode = getWindowingMode(mTasks.get(taskInfo.taskId).first);
+ mTasks.remove(taskInfo.taskId);
+ ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(prevWinMode);
+ if (listeners != null) {
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onTaskVanished(taskInfo);
+ }
+ }
+ }
+
+ private int getWindowingMode(RunningTaskInfo taskInfo) {
+ return taskInfo.configuration.windowConfiguration.getWindowingMode();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/ShellTaskOrganizerTests.java
new file mode 100644
index 000000000000..10672c8d87ad
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.res.Configuration;
+import android.view.SurfaceControl;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for the shell task organizer.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ShellTaskOrganizerTests {
+
+ ShellTaskOrganizer mOrganizer;
+
+ private class TrackingTaskListener implements ShellTaskOrganizer.TaskListener {
+ final ArrayList<RunningTaskInfo> appeared = new ArrayList<>();
+ final ArrayList<RunningTaskInfo> vanished = new ArrayList<>();
+ final ArrayList<RunningTaskInfo> infoChanged = new ArrayList<>();
+
+ @Override
+ public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+ appeared.add(taskInfo);
+ }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ infoChanged.add(taskInfo);
+ }
+
+ @Override
+ public void onTaskVanished(RunningTaskInfo taskInfo) {
+ vanished.add(taskInfo);
+ }
+
+ @Override
+ public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
+ // Not currently used
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mOrganizer = new ShellTaskOrganizer();
+ }
+
+ @Test
+ public void testAppearedVanished() {
+ RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+ TrackingTaskListener listener = new TrackingTaskListener();
+ mOrganizer.addListener(listener, WINDOWING_MODE_MULTI_WINDOW);
+ mOrganizer.onTaskAppeared(taskInfo, null);
+ assertTrue(listener.appeared.contains(taskInfo));
+
+ mOrganizer.onTaskVanished(taskInfo);
+ assertTrue(listener.vanished.contains(taskInfo));
+ }
+
+ @Test
+ public void testAddListenerExistingTasks() {
+ RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+ mOrganizer.onTaskAppeared(taskInfo, null);
+
+ TrackingTaskListener listener = new TrackingTaskListener();
+ mOrganizer.addListener(listener, WINDOWING_MODE_MULTI_WINDOW);
+ assertTrue(listener.appeared.contains(taskInfo));
+ }
+
+ @Test
+ public void testWindowingModeChange() {
+ RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+ TrackingTaskListener mwListener = new TrackingTaskListener();
+ TrackingTaskListener pipListener = new TrackingTaskListener();
+ mOrganizer.addListener(mwListener, WINDOWING_MODE_MULTI_WINDOW);
+ mOrganizer.addListener(pipListener, WINDOWING_MODE_PINNED);
+ mOrganizer.onTaskAppeared(taskInfo, null);
+ assertTrue(mwListener.appeared.contains(taskInfo));
+ assertTrue(pipListener.appeared.isEmpty());
+
+ taskInfo = createTaskInfo(WINDOWING_MODE_PINNED);
+ mOrganizer.onTaskInfoChanged(taskInfo);
+ assertTrue(mwListener.vanished.contains(taskInfo));
+ assertTrue(pipListener.appeared.contains(taskInfo));
+ }
+
+ private RunningTaskInfo createTaskInfo(int windowingMode) {
+ RunningTaskInfo taskInfo = new RunningTaskInfo();
+ taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
+ return taskInfo;
+ }
+}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 4d7e5dfea4f7..dfb4009b07e2 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -37,7 +37,6 @@
#include <androidfw/TypeWrappers.h>
#include <cutils/atomic.h>
#include <utils/ByteOrder.h>
-#include <utils/Debug.h>
#include <utils/Log.h>
#include <utils/String16.h>
#include <utils/String8.h>
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0b13754271b9..90d2537d97a8 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -464,6 +464,7 @@ cc_defaults {
"RootRenderNode.cpp",
"shader/Shader.cpp",
"shader/BitmapShader.cpp",
+ "shader/BlurShader.cpp",
"shader/ComposeShader.cpp",
"shader/LinearGradientShader.cpp",
"shader/RadialGradientShader.cpp",
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 9b1972ed664f..7cb77233846f 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -7,6 +7,7 @@
#include "include/effects/SkRuntimeEffect.h"
#include "shader/Shader.h"
#include "shader/BitmapShader.h"
+#include "shader/BlurShader.h"
#include "shader/ComposeShader.h"
#include "shader/LinearGradientShader.h"
#include "shader/RadialGradientShader.h"
@@ -222,6 +223,22 @@ static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
///////////////////////////////////////////////////////////////////////////////////////////////
+static jlong BlurShader_create(JNIEnv* env , jobject o, jlong matrixPtr, jfloat sigmaX,
+ jfloat sigmaY, jlong shaderHandle) {
+ auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+ auto* inputShader = reinterpret_cast<Shader*>(shaderHandle);
+
+ auto* blurShader = new BlurShader(
+ sigmaX,
+ sigmaY,
+ inputShader,
+ matrix
+ );
+ return reinterpret_cast<jlong>(blurShader);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) {
auto* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory);
@@ -273,6 +290,10 @@ static const JNINativeMethod gBitmapShaderMethods[] = {
{ "nativeCreate", "(JJII)J", (void*)BitmapShader_constructor },
};
+static const JNINativeMethod gBlurShaderMethods[] = {
+ { "nativeCreate", "(JFFJ)J", (void*)BlurShader_create }
+};
+
static const JNINativeMethod gLinearGradientMethods[] = {
{ "nativeCreate", "(JFFFF[J[FIJ)J", (void*)LinearGradient_create },
};
@@ -304,6 +325,8 @@ int register_android_graphics_Shader(JNIEnv* env)
NELEM(gShaderMethods));
android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
NELEM(gBitmapShaderMethods));
+ android::RegisterMethodsOrDie(env, "android/graphics/BlurShader", gBlurShaderMethods,
+ NELEM(gBlurShaderMethods));
android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
NELEM(gLinearGradientMethods));
android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index fc594da19708..e817ca744c58 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -184,7 +184,9 @@ static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz,
proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
}
proxy->setSurface(window, enableTimeout);
- ANativeWindow_release(window);
+ if (window) {
+ ANativeWindow_release(window);
+ }
}
static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz,
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index aad0cca80cdc..b51f6dcfc66f 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -77,10 +77,10 @@ void RenderProxy::setName(const char* name) {
}
void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
- ANativeWindow_acquire(window);
+ if (window) { ANativeWindow_acquire(window); }
mRenderThread.queue().post([this, win = window, enableTimeout]() mutable {
mContext->setSurface(win, enableTimeout);
- ANativeWindow_release(win);
+ if (win) { ANativeWindow_release(win); }
});
}
diff --git a/libs/hwui/shader/BlurShader.cpp b/libs/hwui/shader/BlurShader.cpp
new file mode 100644
index 000000000000..4d18cdd27e4e
--- /dev/null
+++ b/libs/hwui/shader/BlurShader.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#include "BlurShader.h"
+#include "SkImageFilters.h"
+#include "SkRefCnt.h"
+#include "utils/Blur.h"
+
+namespace android::uirenderer {
+BlurShader::BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix)
+ : Shader(matrix)
+ , skImageFilter(
+ SkImageFilters::Blur(
+ Blur::convertRadiusToSigma(radiusX),
+ Blur::convertRadiusToSigma(radiusY),
+ inputShader ? inputShader->asSkImageFilter() : nullptr)
+ ) { }
+
+sk_sp<SkImageFilter> BlurShader::makeSkImageFilter() {
+ return skImageFilter;
+}
+
+BlurShader::~BlurShader() {}
+
+} // namespace android::uirenderer \ No newline at end of file
diff --git a/libs/hwui/shader/BlurShader.h b/libs/hwui/shader/BlurShader.h
new file mode 100644
index 000000000000..9eb22bd11f4a
--- /dev/null
+++ b/libs/hwui/shader/BlurShader.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma once
+#include "Shader.h"
+
+namespace android::uirenderer {
+
+/**
+ * Shader implementation that blurs another Shader instance or the source bitmap
+ */
+class BlurShader : public Shader {
+public:
+ /**
+ * Creates a BlurShader instance with the provided radius values to blur along the x and y
+ * axis accordingly.
+ *
+ * This will blur the contents of the provided input shader if it is non-null, otherwise
+ * the source bitmap will be blurred instead.
+ */
+ BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix);
+ ~BlurShader() override;
+protected:
+ sk_sp<SkImageFilter> makeSkImageFilter() override;
+private:
+ sk_sp<SkImageFilter> skImageFilter;
+};
+
+} // namespace android::uirenderer \ No newline at end of file
diff --git a/libs/hwui/shader/Shader.h b/libs/hwui/shader/Shader.h
index 3c0cdaae8253..6403e1147ded 100644
--- a/libs/hwui/shader/Shader.h
+++ b/libs/hwui/shader/Shader.h
@@ -32,7 +32,9 @@ namespace android::uirenderer {
class Shader: public SkRefCnt {
public:
/**
- * Creates a Shader instance with an optional transformation matrix
+ * Creates a Shader instance with an optional transformation matrix. The transformation matrix
+ * is copied internally and ownership is unchanged. It is the responsibility of the caller to
+ * deallocate it appropriately.
* @param matrix Optional matrix to transform the underlying SkShader or SkImageFilter
*/
Shader(const SkMatrix* matrix);
diff --git a/location/java/android/location/timezone/LocationTimeZoneEvent.java b/location/java/android/location/timezone/LocationTimeZoneEvent.java
index ea3353c245e5..540bdfffe16a 100644
--- a/location/java/android/location/timezone/LocationTimeZoneEvent.java
+++ b/location/java/android/location/timezone/LocationTimeZoneEvent.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import java.util.ArrayList;
import java.util.Collections;
@@ -37,7 +38,7 @@ public final class LocationTimeZoneEvent implements Parcelable {
@IntDef({ EVENT_TYPE_UNKNOWN, EVENT_TYPE_SUCCESS, EVENT_TYPE_SUCCESS })
@interface EventType {}
- /** Uninitialized value for {@link #mEventType} */
+ /** Uninitialized value for {@link #mEventType} - must not be used for real events. */
private static final int EVENT_TYPE_UNKNOWN = 0;
/**
@@ -60,6 +61,9 @@ public final class LocationTimeZoneEvent implements Parcelable {
private static final int EVENT_TYPE_MAX = EVENT_TYPE_UNCERTAIN;
+ @NonNull
+ private final UserHandle mUserHandle;
+
@EventType
private final int mEventType;
@@ -68,8 +72,9 @@ public final class LocationTimeZoneEvent implements Parcelable {
private final long mElapsedRealtimeNanos;
- private LocationTimeZoneEvent(@EventType int eventType, @NonNull List<String> timeZoneIds,
- long elapsedRealtimeNanos) {
+ private LocationTimeZoneEvent(@NonNull UserHandle userHandle, @EventType int eventType,
+ @NonNull List<String> timeZoneIds, long elapsedRealtimeNanos) {
+ mUserHandle = Objects.requireNonNull(userHandle);
mEventType = checkValidEventType(eventType);
mTimeZoneIds = immutableList(timeZoneIds);
@@ -83,6 +88,14 @@ public final class LocationTimeZoneEvent implements Parcelable {
}
/**
+ * Returns the current user when the event was generated.
+ */
+ @NonNull
+ public UserHandle getUserHandle() {
+ return mUserHandle;
+ }
+
+ /**
* Returns the time of this fix, in elapsed real-time since system boot.
*
* <p>This value can be reliably compared to {@link
@@ -117,7 +130,8 @@ public final class LocationTimeZoneEvent implements Parcelable {
@Override
public String toString() {
return "LocationTimeZoneEvent{"
- + "mEventType=" + mEventType
+ + "mUserHandle=" + mUserHandle
+ + ", mEventType=" + mEventType
+ ", mTimeZoneIds=" + mTimeZoneIds
+ ", mElapsedRealtimeNanos=" + mElapsedRealtimeNanos
+ '}';
@@ -127,12 +141,14 @@ public final class LocationTimeZoneEvent implements Parcelable {
new Parcelable.Creator<LocationTimeZoneEvent>() {
@Override
public LocationTimeZoneEvent createFromParcel(Parcel in) {
+ UserHandle userHandle = UserHandle.readFromParcel(in);
int eventType = in.readInt();
@SuppressWarnings("unchecked")
ArrayList<String> timeZoneIds =
(ArrayList<String>) in.readArrayList(null /* classLoader */);
long elapsedRealtimeNanos = in.readLong();
- return new LocationTimeZoneEvent(eventType, timeZoneIds, elapsedRealtimeNanos);
+ return new LocationTimeZoneEvent(
+ userHandle, eventType, timeZoneIds, elapsedRealtimeNanos);
}
@Override
@@ -148,6 +164,7 @@ public final class LocationTimeZoneEvent implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ mUserHandle.writeToParcel(parcel, flags);
parcel.writeInt(mEventType);
parcel.writeList(mTimeZoneIds);
parcel.writeLong(mElapsedRealtimeNanos);
@@ -162,19 +179,21 @@ public final class LocationTimeZoneEvent implements Parcelable {
return false;
}
LocationTimeZoneEvent that = (LocationTimeZoneEvent) o;
- return mEventType == that.mEventType
+ return mUserHandle.equals(that.mUserHandle)
+ && mEventType == that.mEventType
&& mElapsedRealtimeNanos == that.mElapsedRealtimeNanos
&& mTimeZoneIds.equals(that.mTimeZoneIds);
}
@Override
public int hashCode() {
- return Objects.hash(mEventType, mTimeZoneIds, mElapsedRealtimeNanos);
+ return Objects.hash(mUserHandle, mEventType, mTimeZoneIds, mElapsedRealtimeNanos);
}
/** @hide */
public static final class Builder {
+ private UserHandle mUserHandle;
private @EventType int mEventType = EVENT_TYPE_UNKNOWN;
private @NonNull List<String> mTimeZoneIds = Collections.emptyList();
private long mElapsedRealtimeNanos;
@@ -186,13 +205,22 @@ public final class LocationTimeZoneEvent implements Parcelable {
* Sets the contents of this from the supplied instance.
*/
public Builder(@NonNull LocationTimeZoneEvent ltz) {
+ mUserHandle = ltz.mUserHandle;
mEventType = ltz.mEventType;
mTimeZoneIds = ltz.mTimeZoneIds;
mElapsedRealtimeNanos = ltz.mElapsedRealtimeNanos;
}
/**
- * Set the time zone ID of this fix.
+ * Set the current user when this event was generated.
+ */
+ public Builder setUserHandle(@NonNull UserHandle userHandle) {
+ mUserHandle = Objects.requireNonNull(userHandle);
+ return this;
+ }
+
+ /**
+ * Set the time zone ID of this event.
*/
public Builder setEventType(@EventType int eventType) {
checkValidEventType(eventType);
@@ -201,7 +229,7 @@ public final class LocationTimeZoneEvent implements Parcelable {
}
/**
- * Sets the time zone IDs of this fix.
+ * Sets the time zone IDs of this event.
*/
public Builder setTimeZoneIds(@NonNull List<String> timeZoneIds) {
mTimeZoneIds = Objects.requireNonNull(timeZoneIds);
@@ -209,9 +237,7 @@ public final class LocationTimeZoneEvent implements Parcelable {
}
/**
- * Sets the time of this fix, in elapsed real-time since system boot.
- *
- * @param time elapsed real-time of fix, in nanoseconds since system boot.
+ * Sets the time of this event, in elapsed real-time since system boot.
*/
public Builder setElapsedRealtimeNanos(long time) {
mElapsedRealtimeNanos = time;
@@ -222,8 +248,8 @@ public final class LocationTimeZoneEvent implements Parcelable {
* Builds a {@link LocationTimeZoneEvent} instance.
*/
public LocationTimeZoneEvent build() {
- return new LocationTimeZoneEvent(this.mEventType, this.mTimeZoneIds,
- this.mElapsedRealtimeNanos);
+ return new LocationTimeZoneEvent(
+ mUserHandle, mEventType, mTimeZoneIds, mElapsedRealtimeNanos);
}
}
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
index 0143c88e0898..c533c20d07e3 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
@@ -32,7 +32,7 @@ import java.util.Objects;
/**
* Base class for location time zone providers implemented as unbundled services.
*
- * TODO Provide details of the expected service actions.
+ * TODO(b/152744911): Provide details of the expected service actions and threading.
*
* <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain
* API stable.
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
index dd80466637e6..e898bbf3ecc0 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
@@ -44,6 +44,24 @@ public final class LocationTimeZoneProviderRequestUnbundled {
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ LocationTimeZoneProviderRequestUnbundled that =
+ (LocationTimeZoneProviderRequestUnbundled) o;
+ return mRequest.equals(that.mRequest);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRequest);
+ }
+
+ @Override
public String toString() {
return mRequest.toString();
}
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index e90fbd4dead8..cfdb48bb4476 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11901,8 +11901,8 @@ package android.content.pm {
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
field public static final int INVALID_ID = -1; // 0xffffffff
field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
+ field public static final int STAGED_SESSION_CONFLICT = 4; // 0x4
field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
- field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
}
@@ -14250,6 +14250,10 @@ package android.graphics {
enum_constant public static final android.graphics.BlurMaskFilter.Blur SOLID;
}
+ public final class BlurShader extends android.graphics.Shader {
+ ctor public BlurShader(float, float, @Nullable android.graphics.Shader);
+ }
+
public class Camera {
ctor public Camera();
method public void applyToCanvas(android.graphics.Canvas);
@@ -35282,9 +35286,11 @@ package android.os {
public final class PowerManager {
method public void addThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
method public void addThermalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.PowerManager.OnThermalStatusChangedListener);
+ method @Nullable public java.time.Duration getBatteryDischargePrediction();
method public int getCurrentThermalStatus();
method public int getLocationPowerSaveMode();
method public float getThermalHeadroom(@IntRange(from=0, to=60) int);
+ method public boolean isBatteryDischargePredictionPersonalized();
method public boolean isDeviceIdleMode();
method public boolean isIgnoringBatteryOptimizations(String);
method public boolean isInteractive();
@@ -46502,7 +46508,9 @@ package android.telephony {
method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
method public boolean isConcurrentVoiceAndDataSupported();
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
method public boolean isEmergencyNumber(@NonNull String);
method public boolean isHearingAidCompatibilitySupported();
@@ -46524,6 +46532,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledForReason(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>);
method public boolean setLine1NumberForDisplay(String, String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNetworkSelectionModeAutomatic();
@@ -46571,6 +46580,10 @@ package android.telephony {
field public static final int DATA_CONNECTING = 1; // 0x1
field public static final int DATA_DISCONNECTED = 0; // 0x0
field public static final int DATA_DISCONNECTING = 4; // 0x4
+ field public static final int DATA_ENABLED_REASON_CARRIER = 2; // 0x2
+ field public static final int DATA_ENABLED_REASON_POLICY = 1; // 0x1
+ field public static final int DATA_ENABLED_REASON_THERMAL = 3; // 0x3
+ field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
field public static final int DATA_UNKNOWN = -1; // 0xffffffff
field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
@@ -53328,7 +53341,7 @@ package android.view {
}
public class ViewPropertyAnimator {
- method public android.view.ViewPropertyAnimator alpha(float);
+ method public android.view.ViewPropertyAnimator alpha(@FloatRange(from=0.0f, to=1.0f) float);
method public android.view.ViewPropertyAnimator alphaBy(float);
method public void cancel();
method public long getDuration();
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 3fd0ee1c8011..0cfad1c769b1 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -7298,6 +7298,7 @@ package android.os {
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void suppressAmbientDisplay(@NonNull String, boolean);
@@ -10099,10 +10100,8 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
method public boolean isDataConnectivityPossible();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledWithReason(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
@@ -10134,7 +10133,6 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledWithReason(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
@@ -10172,10 +10170,6 @@ package android.telephony {
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
- field public static final int DATA_ENABLED_REASON_CARRIER = 2; // 0x2
- field public static final int DATA_ENABLED_REASON_POLICY = 1; // 0x1
- field public static final int DATA_ENABLED_REASON_THERMAL = 3; // 0x3
- field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
@@ -11585,8 +11579,8 @@ package android.webkit {
public interface PacProcessor {
method @Nullable public String findProxyForUrl(@NonNull String);
method @NonNull public static android.webkit.PacProcessor getInstance();
- method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(long);
- method public default long getNetworkHandle();
+ method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(@Nullable android.net.Network);
+ method @Nullable public default android.net.Network getNetwork();
method public default void releasePacProcessor();
method public boolean setProxyScript(@NonNull String);
}
@@ -11727,7 +11721,7 @@ package android.webkit {
method public android.webkit.CookieManager getCookieManager();
method public android.webkit.GeolocationPermissions getGeolocationPermissions();
method @NonNull public default android.webkit.PacProcessor getPacProcessor();
- method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(long);
+ method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(@Nullable android.net.Network);
method public android.webkit.ServiceWorkerController getServiceWorkerController();
method public android.webkit.WebViewFactoryProvider.Statics getStatics();
method @Deprecated public android.webkit.TokenBindingService getTokenBindingService();
diff --git a/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml
index b8e1edc46da7..20aa5f79c5cc 100644
--- a/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml
+++ b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml
@@ -22,7 +22,8 @@
<item target="attr/icon" value="@attr/icon"/>
<item target="attr/selectedIcon" value="@attr/selectedIcon"/>
- <item target="attr/intent" value="@attr/longIntent"/>
+ <item target="attr/intent" value="@attr/intent"/>
+ <item target="attr/longIntent" value="@attr/longIntent"/>
<item target="attr/componentNames" value="@attr/componentNames"/>
<item target="attr/highlightWhenSelected" value="@attr/highlightWhenSelected"/>
<item target="attr/categories" value="@attr/categories"/>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java
index b2f98ecde513..24d9d09d2ca9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java
@@ -22,7 +22,6 @@ import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.SystemServicesModule;
import com.android.systemui.dagger.SystemUIModule;
-import com.android.systemui.onehanded.dagger.OneHandedModule;
import com.android.systemui.pip.phone.dagger.PipModule;
import dagger.Subcomponent;
@@ -36,7 +35,6 @@ import dagger.Subcomponent;
DependencyProvider.class,
DependencyBinder.class,
PipModule.class,
- OneHandedModule.class,
SystemServicesModule.class,
SystemUIModule.class,
CarSystemUIModule.class,
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index f80e934cf37c..9ff868467531 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -352,16 +352,18 @@ public class DynamicSystemInstallationService extends Service
if (powerManager != null) {
powerManager.reboot("dynsystem");
}
- } else {
- Log.e(TAG, "Failed to enable DynamicSystem because of native runtime error.");
- mNM.cancel(NOTIFICATION_ID);
+ return;
+ }
- Toast.makeText(this,
- getString(R.string.toast_failed_to_reboot_to_dynsystem),
- Toast.LENGTH_LONG).show();
+ Log.e(TAG, "Failed to enable DynamicSystem because of native runtime error.");
- mDynSystem.remove();
- }
+ Toast.makeText(this,
+ getString(R.string.toast_failed_to_reboot_to_dynsystem),
+ Toast.LENGTH_LONG).show();
+
+ postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_EXCEPTION, null);
+ resetTaskAndStop();
+ mDynSystem.remove();
}
private void executeRebootToNormalCommand() {
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index f219d248e641..8b92640e9996 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -37,7 +37,7 @@
<string name="wifi_no_internet" msgid="1774198889176926299">"Ինտերնետ կապ չկա"</string>
<string name="saved_network" msgid="7143698034077223645">"Ով է պահել՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ավտոմատ կերպով կապակցվել է %1$s-ի միջոցով"</string>
- <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ավտոմատ կերպով միացել է ցանցի վարկանիշի ծառայության մատակարարի միջոցով"</string>
+ <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ավտոմատ միացել է ցանցերի վարկանիշի մատակարարի միջոցով"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Միացված է %1$s-ի միջոցով"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Միացված է <xliff:g id="NAME">%1$s</xliff:g>-ի միջոցով"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Հասանելի է %1$s-ի միջոցով"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 83b72e925fe1..27f5dc9cc4d4 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -93,8 +93,8 @@
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Sim-toegang"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-audio"</string>
- <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Gehoorapparaten"</string>
- <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Verbonden met gehoorapparaten"</string>
+ <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hoortoestellen"</string>
+ <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Verbonden met hoortoestellen"</string>
<string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Verbonden met audio van medium"</string>
<string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Verbonden met audio van telefoon"</string>
<string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Verbonden met server voor bestandsoverdracht"</string>
@@ -111,7 +111,7 @@
<string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Gebruiken voor audio van telefoon"</string>
<string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Gebruiken voor bestandsoverdracht"</string>
<string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gebruiken voor invoer"</string>
- <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gebruiken voor gehoorapparaten"</string>
+ <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gebruiken voor hoortoestellen"</string>
<string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Koppelen"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"KOPPELEN"</string>
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Annuleren"</string>
@@ -127,8 +127,8 @@
<string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Hoofdtelefoon"</string>
<string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Randapparaat voor invoer"</string>
<string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string>
- <string name="bluetooth_hearingaid_left_pairing_message" msgid="8561855779703533591">"Linker gehoorapparaat koppelen…"</string>
- <string name="bluetooth_hearingaid_right_pairing_message" msgid="2655347721696331048">"Rechter gehoorapparaat koppelen…"</string>
+ <string name="bluetooth_hearingaid_left_pairing_message" msgid="8561855779703533591">"Linker hoortoestel koppelen…"</string>
+ <string name="bluetooth_hearingaid_right_pairing_message" msgid="2655347721696331048">"Rechter hoortoestel koppelen…"</string>
<string name="bluetooth_hearingaid_left_battery_level" msgid="7375621694748104876">"Links: batterijniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"Rechts: batterijniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi: uitgeschakeld."</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 66be8dd68283..a29314817a8f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -860,9 +860,6 @@ class SettingsProtoDumpUtil {
p.end(intentFirewallToken);
dumpSetting(s, p,
- Settings.Global.JOB_SCHEDULER_CONSTANTS,
- GlobalSettingsProto.JOB_SCHEDULER_CONSTANTS);
- dumpSetting(s, p,
Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
GlobalSettingsProto.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 0ac3355c4952..f219aecfc9d9 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -309,7 +309,6 @@ public class SettingsBackupTest {
Settings.Global.INSTANT_APP_DEXOPT_ENABLED,
Settings.Global.INTENT_FIREWALL_UPDATE_CONTENT_URL,
Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
- Settings.Global.JOB_SCHEDULER_CONSTANTS,
Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
Settings.Global.KERNEL_CPU_THREAD_READER,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4ce9f5a9edc6..af008b996172 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -113,6 +113,7 @@
<uses-permission android:name="android.permission.SET_ORIENTATION" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.MONITOR_INPUT" />
+ <uses-permission android:name="android.permission.INPUT_CONSUMER" />
<!-- DreamManager -->
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index b42d71abaa6d..df66bf5a1051 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -45,4 +45,5 @@
-keep class com.android.systemui.dagger.GlobalRootComponent { *; }
-keep class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { *; }
--keep class com.android.systemui.dagger.Dagger** { *; } \ No newline at end of file
+-keep class com.android.systemui.dagger.Dagger** { *; }
+-keep class com.android.systemui.tv.Dagger** { *; } \ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/face_auth_wallpaper.png b/packages/SystemUI/res-keyguard/drawable/face_auth_wallpaper.png
new file mode 100644
index 000000000000..b907f4eaf362
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/face_auth_wallpaper.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/bubble_stack_user_education.xml b/packages/SystemUI/res/layout/bubble_stack_user_education.xml
index 616403219bc6..fe1ed4b6f726 100644
--- a/packages/SystemUI/res/layout/bubble_stack_user_education.xml
+++ b/packages/SystemUI/res/layout/bubble_stack_user_education.xml
@@ -15,8 +15,8 @@
~ limitations under the License.
-->
<LinearLayout
+ android:id="@+id/stack_education_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/user_education_view"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingTop="48dp"
@@ -25,26 +25,29 @@
android:paddingEnd="16dp"
android:layout_marginEnd="24dp"
android:orientation="vertical"
- android:background="@drawable/bubble_stack_user_education_bg">
-
+ android:background="@drawable/bubble_stack_user_education_bg"
+ >
<TextView
- android:id="@+id/user_education_title"
+ android:id="@+id/stack_education_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
android:maxLines="2"
android:ellipsize="end"
+ android:gravity="start"
+ android:textAlignment="viewStart"
android:text="@string/bubbles_user_education_title"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/>
<TextView
- android:id="@+id/user_education_description"
+ android:id="@+id/stack_education_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
+ android:gravity="start"
+ android:textAlignment="viewStart"
android:text="@string/bubbles_user_education_description"
android:fontFamily="@*android:string/config_bodyFontFamily"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
-
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
index 213bb923db65..b51dc93dc373 100644
--- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
+++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
@@ -14,77 +14,77 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.bubbles.ManageEducationView
+
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:id="@+id/manage_education_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:paddingTop="28dp"
+ android:paddingBottom="16dp"
+ android:paddingStart="@dimen/bubble_expanded_view_padding"
+ android:paddingEnd="48dp"
+ android:layout_marginEnd="24dp"
+ android:orientation="vertical"
+ android:background="@drawable/bubble_stack_user_education_bg"
>
- <LinearLayout
+
+ <TextView
+ android:id="@+id/user_education_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:id="@+id/manage_education_view"
- android:clickable="true"
- android:paddingTop="28dp"
+ android:paddingStart="16dp"
android:paddingBottom="16dp"
- android:paddingStart="@dimen/bubble_expanded_view_padding"
- android:paddingEnd="48dp"
- android:layout_marginEnd="24dp"
- android:orientation="vertical"
- android:background="@drawable/bubble_stack_user_education_bg"
- >
+ android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:gravity="start"
+ android:textAlignment="viewStart"
+ android:text="@string/bubbles_user_education_manage_title"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/>
- <TextView
- android:id="@+id/user_education_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="16dp"
- android:paddingBottom="16dp"
- android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
- android:maxLines="2"
- android:ellipsize="end"
- android:text="@string/bubbles_user_education_manage_title"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/>
+ <TextView
+ android:id="@+id/user_education_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingStart="16dp"
+ android:paddingBottom="24dp"
+ android:text="@string/bubbles_user_education_manage"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:gravity="start"
+ android:textAlignment="viewStart"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
- <TextView
- android:id="@+id/user_education_description"
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:id="@+id/button_layout"
+ android:orientation="horizontal" >
+
+ <com.android.systemui.statusbar.AlphaOptimizedButton
+ style="@android:style/Widget.Material.Button.Borderless"
+ android:id="@+id/manage"
+ android:layout_gravity="start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingStart="16dp"
- android:paddingBottom="24dp"
- android:text="@string/bubbles_user_education_manage"
- android:maxLines="2"
- android:ellipsize="end"
- android:fontFamily="@*android:string/config_bodyFontFamily"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
+ android:focusable="true"
+ android:clickable="false"
+ android:text="@string/manage_bubbles_text"
+ android:textColor="?attr/wallpaperTextColor"
+ />
- <LinearLayout
- android:layout_height="wrap_content"
+ <com.android.systemui.statusbar.AlphaOptimizedButton
+ style="@android:style/Widget.Material.Button.Borderless"
+ android:id="@+id/got_it"
+ android:layout_gravity="start"
android:layout_width="wrap_content"
- android:id="@+id/button_layout"
- android:orientation="horizontal" >
-
- <com.android.systemui.statusbar.AlphaOptimizedButton
- style="@android:style/Widget.Material.Button.Borderless"
- android:id="@+id/manage"
- android:layout_gravity="start"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:clickable="false"
- android:text="@string/manage_bubbles_text"
- android:textColor="?attr/wallpaperTextColor"
- />
-
- <com.android.systemui.statusbar.AlphaOptimizedButton
- style="@android:style/Widget.Material.Button.Borderless"
- android:id="@+id/got_it"
- android:layout_gravity="start"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:text="@string/bubbles_user_education_got_it"
- android:textColor="?attr/wallpaperTextColor"
- />
- </LinearLayout>
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:text="@string/bubbles_user_education_got_it"
+ android:textColor="?attr/wallpaperTextColor"
+ />
</LinearLayout>
-</com.android.systemui.bubbles.ManageEducationView>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index 2c4b937ce95b..fd89c0bf39dd 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -210,7 +210,7 @@
android:clickable="false"
android:focusable="false"
android:ellipsize="end"
- android:maxLines="3"
+ android:maxLines="4"
android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/>
</com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index 07951705664a..c353d089895c 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -128,6 +128,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
+ android:textDirection="locale"
style="@style/TextAppearance.NotificationImportanceChannelGroup" />
</LinearLayout>
</com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 59af458e2402..17b840cc7a20 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -162,8 +162,8 @@ public class ExpandHelper implements Gefingerpoken {
*
* @param context application context
* @param callback the container that holds the items to be manipulated
- * @param small the smallest allowable size for the manuipulated items.
- * @param large the largest allowable size for the manuipulated items.
+ * @param small the smallest allowable size for the manipulated items.
+ * @param large the largest allowable size for the manipulated items.
*/
public ExpandHelper(Context context, Callback callback, int small, int large) {
mSmallSize = small;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index d4e6506bd9a0..0892612d1825 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -111,7 +111,7 @@ public class AuthCredentialPasswordView extends AuthCredentialView
// VerifyCredentialResponse so that we can request a Gatekeeper HAT with the
// Gatekeeper Password and operationId.
mPendingLockCheck = LockPatternChecker.verifyCredential(mLockPatternUtils,
- password, mEffectiveUserId, LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW,
+ password, mEffectiveUserId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
this::onCredentialVerified);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
index ad89ae82f637..ab8162f9464d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -75,7 +75,7 @@ public class AuthCredentialPatternView extends AuthCredentialView {
mLockPatternUtils,
credential,
mEffectiveUserId,
- LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW,
+ LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
this::onPatternVerified);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index a8f6f85201f6..b44ff294b792 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -292,10 +292,12 @@ public abstract class AuthCredentialView extends LinearLayout {
// The response passed into this method contains the Gatekeeper Password. We still
// have to request Gatekeeper to create a Hardware Auth Token with the
// Gatekeeper Password and Challenge (keystore operationId in this case)
- final VerifyCredentialResponse gkResponse = mLockPatternUtils.verifyGatekeeperPassword(
- response.getGatekeeperPw(), mOperationId, mEffectiveUserId);
+ final long pwHandle = response.getGatekeeperPasswordHandle();
+ final VerifyCredentialResponse gkResponse = mLockPatternUtils
+ .verifyGatekeeperPasswordHandle(pwHandle, mOperationId, mEffectiveUserId);
mCallback.onCredentialMatched(gkResponse.getGatekeeperHAT());
+ mLockPatternUtils.removeGatekeeperPasswordHandle(pwHandle);
} else {
if (timeoutMs > 0) {
mHandler.removeCallbacks(mClearErrorRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index ec9644af7013..64df2b99ee22 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -19,11 +19,7 @@ package com.android.systemui.bubbles;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION;
-import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION;
import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
-import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -38,7 +34,6 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Outline;
@@ -54,7 +49,6 @@ import android.provider.Settings;
import android.util.Log;
import android.view.Choreographer;
import android.view.DisplayCutout;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceControl;
@@ -70,10 +64,8 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.TextView;
-import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -82,7 +74,6 @@ import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Interpolators;
import com.android.systemui.Prefs;
import com.android.systemui.R;
@@ -115,10 +106,6 @@ public class BubbleStackView extends FrameLayout
implements ViewTreeObserver.OnComputeInternalInsetsListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
- /** Animation durations for bubble stack user education views. **/
- static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
- private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40;
-
/** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
static final float FLYOUT_DRAG_PERCENT_DISMISS = 0.25f;
@@ -556,7 +543,7 @@ public class BubbleStackView extends FrameLayout
// 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()) {
+ if (!maybeShowStackEdu()) {
mBubbleData.setExpanded(!mBubbleData.isExpanded());
}
}
@@ -582,7 +569,9 @@ public class BubbleStackView extends FrameLayout
}
if (mBubbleData.isExpanded()) {
- maybeShowManageEducation(false /* show */);
+ if (mManageEduView != null) {
+ mManageEduView.hide(false /* show */);
+ }
// If we're expanded, tell the animation controller to prepare to drag this bubble,
// dispatching to the individual bubble magnet listener.
@@ -637,7 +626,9 @@ public class BubbleStackView extends FrameLayout
mExpandedAnimationController.dragBubbleOut(
v, viewInitialX + dx, viewInitialY + dy);
} else {
- hideStackUserEducation(false /* fromExpansion */);
+ if (mStackEduView != null) {
+ mStackEduView.hide(false /* fromExpansion */);
+ }
mStackAnimationController.moveStackFromTouch(
viewInitialX + dx, viewInitialY + dy);
}
@@ -684,7 +675,7 @@ public class BubbleStackView extends FrameLayout
private OnClickListener mFlyoutClickListener = new OnClickListener() {
@Override
public void onClick(View view) {
- if (maybeShowStackUserEducation()) {
+ if (maybeShowStackEdu()) {
// If we're showing user education, don't open the bubble show the education first
mBubbleToExpandAfterFlyoutCollapse = null;
} else {
@@ -728,7 +719,7 @@ public class BubbleStackView extends FrameLayout
mFlyout.removeCallbacks(mHideFlyout);
animateFlyoutCollapsed(shouldDismiss, velX);
- maybeShowStackUserEducation();
+ maybeShowStackEdu();
}
};
@@ -737,14 +728,8 @@ public class BubbleStackView extends FrameLayout
@Nullable
private BubbleOverflow mBubbleOverflow;
-
- private boolean mShouldShowUserEducation;
- private boolean mAnimatingEducationAway;
- private View mUserEducationView;
-
- private boolean mShouldShowManageEducation;
- private ManageEducationView mManageEducationView;
- private boolean mAnimatingManageEducationAway;
+ private StackEducationView mStackEduView;
+ private ManageEducationView mManageEduView;
private ViewGroup mManageMenu;
private ImageView mManageSettingsIcon;
@@ -805,8 +790,6 @@ public class BubbleStackView extends FrameLayout
onBubbleAnimatedOut);
mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
- setUpUserEducation();
-
// Force LTR by default since most of the Bubbles UI is positioned manually by the user, or
// is centered. It greatly simplifies translation positioning/animations. Views that will
// actually lay out differently in RTL, such as the flyout and expanded view, will set their
@@ -819,6 +802,8 @@ public class BubbleStackView extends FrameLayout
mBubbleContainer.setClipChildren(false);
addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ updateUserEdu();
+
mExpandedViewContainer = new FrameLayout(context);
mExpandedViewContainer.setElevation(elevation);
mExpandedViewContainer.setClipChildren(false);
@@ -1092,48 +1077,66 @@ public class BubbleStackView extends FrameLayout
addView(mManageMenu);
}
- private void setUpUserEducation() {
- if (mUserEducationView != null) {
- removeView(mUserEducationView);
- }
- mShouldShowUserEducation = shouldShowBubblesEducation();
- if (DEBUG_USER_EDUCATION) {
- Log.d(TAG, "shouldShowUserEducation: " + mShouldShowUserEducation);
+ /**
+ * Whether the educational view should show for the expanded view "manage" menu.
+ */
+ private boolean shouldShowManageEdu() {
+ final boolean seen = Prefs.getBoolean(mContext,
+ Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false /* default */);
+ final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
+ && mExpandedBubble != null;
+ if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
+ Log.d(TAG, "Show manage edu: " + shouldShow);
}
- if (mShouldShowUserEducation) {
- mUserEducationView = mInflater.inflate(R.layout.bubble_stack_user_education, this,
- false /* attachToRoot */);
- mUserEducationView.setVisibility(GONE);
-
- final TypedArray ta = mContext.obtainStyledAttributes(
- new int[] {android.R.attr.colorAccent,
- android.R.attr.textColorPrimaryInverse});
- final int bgColor = ta.getColor(0, Color.BLACK);
- int textColor = ta.getColor(1, Color.WHITE);
- ta.recycle();
- textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
+ return shouldShow;
+ }
- TextView title = mUserEducationView.findViewById(R.id.user_education_title);
- TextView description = mUserEducationView.findViewById(R.id.user_education_description);
- title.setTextColor(textColor);
- description.setTextColor(textColor);
+ private void maybeShowManageEdu() {
+ if (!shouldShowManageEdu()) {
+ return;
+ }
+ if (mManageEduView == null) {
+ mManageEduView = new ManageEducationView(mContext);
+ addView(mManageEduView);
+ }
+ mManageEduView.show(mExpandedBubble.getExpandedView(), mTempRect);
+ }
- updateUserEducationForLayoutDirection();
- addView(mUserEducationView);
+ /**
+ * Whether education view should show for the collapsed stack.
+ */
+ private boolean shouldShowStackEdu() {
+ final boolean seen = Prefs.getBoolean(getContext(),
+ Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION, false /* default */);
+ final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext);
+ if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
+ Log.d(TAG, "Show stack edu: " + shouldShow);
}
+ return shouldShow;
+ }
- if (mManageEducationView != null) {
- removeView(mManageEducationView);
+ /**
+ * @return true if education view for collapsed stack should show and was not showing before.
+ */
+ private boolean maybeShowStackEdu() {
+ if (!shouldShowStackEdu()) {
+ return false;
+ }
+ if (mStackEduView == null) {
+ mStackEduView = new StackEducationView(mContext);
+ addView(mStackEduView);
}
- mShouldShowManageEducation = shouldShowManageEducation();
- if (DEBUG_USER_EDUCATION) {
- Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation);
+ return mStackEduView.show(mStackAnimationController.getStartPosition());
+ }
+
+ private void updateUserEdu() {
+ maybeShowStackEdu();
+ if (mManageEduView != null) {
+ mManageEduView.invalidate();
}
- if (mShouldShowManageEducation) {
- mManageEducationView = (ManageEducationView)
- mInflater.inflate(R.layout.bubbles_manage_button_education, this /* root */,
- false /* attachToRoot */);
- addView(mManageEducationView);
+ maybeShowManageEdu();
+ if (mStackEduView != null) {
+ mStackEduView.invalidate();
}
}
@@ -1164,9 +1167,9 @@ public class BubbleStackView extends FrameLayout
*/
public void onThemeChanged() {
setUpFlyout();
- setUpUserEducation();
setUpManageMenu();
updateOverflow();
+ updateUserEdu();
updateExpandedViewTheme();
}
@@ -1197,12 +1200,11 @@ public class BubbleStackView extends FrameLayout
public void onLayoutDirectionChanged(int direction) {
mManageMenu.setLayoutDirection(direction);
mFlyout.setLayoutDirection(direction);
- if (mUserEducationView != null) {
- mUserEducationView.setLayoutDirection(direction);
- updateUserEducationForLayoutDirection();
+ if (mStackEduView != null) {
+ mStackEduView.setLayoutDirection(direction);
}
- if (mManageEducationView != null) {
- mManageEducationView.setLayoutDirection(direction);
+ if (mManageEduView != null) {
+ mManageEduView.setLayoutDirection(direction);
}
updateExpandedViewDirection(direction);
}
@@ -1446,7 +1448,7 @@ public class BubbleStackView extends FrameLayout
Log.d(TAG, "addBubble: " + bubble);
}
- if (getBubbleCount() == 0 && mShouldShowUserEducation) {
+ if (getBubbleCount() == 0 && shouldShowStackEdu()) {
// Override the default stack position if we're showing user education.
mStackAnimationController.setStackPosition(
mStackAnimationController.getStartPosition());
@@ -1649,115 +1651,6 @@ public class BubbleStackView extends FrameLayout
notifyExpansionChanged(mExpandedBubble, mIsExpanded);
}
- /**
- * If necessary, shows the user education view for the bubble stack. This appears the first
- * time a user taps on a bubble.
- *
- * @return true if user education was shown, false otherwise.
- */
- private boolean maybeShowStackUserEducation() {
- if (mShouldShowUserEducation && mUserEducationView.getVisibility() != VISIBLE) {
- mUserEducationView.setAlpha(0);
- mUserEducationView.setVisibility(VISIBLE);
- updateUserEducationForLayoutDirection();
-
- // Post so we have height of mUserEducationView
- mUserEducationView.post(() -> {
- final int viewHeight = mUserEducationView.getHeight();
- PointF stackPosition = mStackAnimationController.getStartPosition();
- final float translationY = stackPosition.y + (mBubbleSize / 2) - (viewHeight / 2);
- mUserEducationView.setTranslationY(translationY);
- mUserEducationView.animate()
- .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
- .setInterpolator(FAST_OUT_SLOW_IN)
- .alpha(1);
- });
- Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, true);
- return true;
- }
- return false;
- }
-
- private void updateUserEducationForLayoutDirection() {
- if (mUserEducationView == null) {
- return;
- }
- LinearLayout textLayout = mUserEducationView.findViewById(R.id.user_education_view);
- TextView title = mUserEducationView.findViewById(R.id.user_education_title);
- TextView description = mUserEducationView.findViewById(R.id.user_education_description);
- boolean isLtr =
- getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR;
- if (isLtr) {
- mUserEducationView.setLayoutDirection(LAYOUT_DIRECTION_LTR);
- textLayout.setBackgroundResource(R.drawable.bubble_stack_user_education_bg);
- title.setGravity(Gravity.LEFT);
- description.setGravity(Gravity.LEFT);
- } else {
- mUserEducationView.setLayoutDirection(LAYOUT_DIRECTION_RTL);
- textLayout.setBackgroundResource(R.drawable.bubble_stack_user_education_bg_rtl);
- title.setGravity(Gravity.RIGHT);
- description.setGravity(Gravity.RIGHT);
- }
- }
-
- /**
- * If necessary, hides the user education view for the bubble stack.
- *
- * @param fromExpansion if true this indicates the hide is happening due to the bubble being
- * expanded, false if due to a touch outside of the bubble stack.
- */
- void hideStackUserEducation(boolean fromExpansion) {
- if (mShouldShowUserEducation
- && mUserEducationView.getVisibility() == VISIBLE
- && !mAnimatingEducationAway) {
- mAnimatingEducationAway = true;
- mUserEducationView.animate()
- .alpha(0)
- .setDuration(fromExpansion
- ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT
- : ANIMATE_STACK_USER_EDUCATION_DURATION)
- .withEndAction(() -> {
- mAnimatingEducationAway = false;
- mShouldShowUserEducation = shouldShowBubblesEducation();
- mUserEducationView.setVisibility(GONE);
- });
- }
- }
-
- /**
- * If necessary, toggles the user education view for the manage button. This is shown when the
- * bubble stack is expanded for the first time.
- *
- * @param show whether the user education view should show or not.
- */
- void maybeShowManageEducation(boolean show) {
- if (mManageEducationView == null) {
- return;
- }
- if (show
- && mShouldShowManageEducation
- && mManageEducationView.getVisibility() != VISIBLE
- && mIsExpanded
- && mExpandedBubble.getExpandedView() != null) {
- mManageEducationView.show(mExpandedBubble.getExpandedView(), mTempRect,
- () -> maybeShowManageEducation(false) /* run on click */);
- Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true);
- } else if (!show
- && mManageEducationView.getVisibility() == VISIBLE
- && !mAnimatingManageEducationAway) {
- mManageEducationView.animate()
- .alpha(0)
- .setDuration(mIsExpansionAnimating
- ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT
- : ANIMATE_STACK_USER_EDUCATION_DURATION)
- .withEndAction(() -> {
- mAnimatingManageEducationAway = false;
- mShouldShowManageEducation = shouldShowManageEducation();
- mManageEducationView.setVisibility(GONE);
- });
- }
- }
-
void showExpandedViewContents(int displayId) {
if (mExpandedBubble != null
&& mExpandedBubble.getExpandedView() != null
@@ -1791,7 +1684,9 @@ public class BubbleStackView extends FrameLayout
cancelDelayedExpandCollapseSwitchAnimations();
mIsExpanded = true;
- hideStackUserEducation(true /* fromExpansion */);
+ if (mStackEduView != null) {
+ mStackEduView.hide(true /* fromExpansion */);
+ }
beforeExpandedViewAnimation();
mBubbleContainer.setActiveController(mExpandedAnimationController);
@@ -1799,7 +1694,9 @@ public class BubbleStackView extends FrameLayout
updatePointerPosition();
mExpandedAnimationController.expandFromStack(() -> {
afterExpandedViewAnimation();
- maybeShowManageEducation(true);
+ if (mIsExpanded && mExpandedBubble.getExpandedView() != null) {
+ maybeShowManageEdu();
+ }
} /* after */);
mExpandedViewContainer.setTranslationX(0);
@@ -1936,7 +1833,9 @@ public class BubbleStackView extends FrameLayout
.withEndActions(() -> {
final BubbleViewProvider previouslySelected = mExpandedBubble;
beforeExpandedViewAnimation();
- maybeShowManageEducation(false);
+ if (mManageEduView != null) {
+ mManageEduView.hide(false /* fromExpansion */);
+ }
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "animateCollapse");
@@ -2104,8 +2003,8 @@ public class BubbleStackView extends FrameLayout
// from any location.
if (!mIsExpanded
|| mShowingManage
- || (mManageEducationView != null
- && mManageEducationView.getVisibility() == VISIBLE)) {
+ || (mManageEduView != null
+ && mManageEduView.getVisibility() == VISIBLE)) {
touchableRegion.setEmpty();
}
}
@@ -2289,7 +2188,7 @@ public class BubbleStackView extends FrameLayout
if (flyoutMessage == null
|| flyoutMessage.message == null
|| !bubble.showFlyout()
- || (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE)
+ || (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE)
|| isExpanded()
|| mIsExpansionAnimating
|| mIsGestureInProgress
@@ -2398,7 +2297,7 @@ public class BubbleStackView extends FrameLayout
* them.
*/
public void getTouchableRegion(Rect outRect) {
- if (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) {
+ if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
// When user education shows then capture all touches
outRect.set(0, 0, getWidth(), getHeight());
return;
@@ -2770,18 +2669,6 @@ public class BubbleStackView extends FrameLayout
return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
}
- /** Whether the educational view should appear for bubbles. **/
- private boolean shouldShowBubblesEducation() {
- return BubbleDebugConfig.forceShowUserEducation(getContext())
- || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, false);
- }
-
- /** Whether the educational view should appear for the expanded view "manage" button. **/
- private boolean shouldShowManageEducation() {
- return BubbleDebugConfig.forceShowUserEducation(getContext())
- || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false);
- }
-
/** For debugging only */
List<Bubble> getBubblesOnScreen() {
List<Bubble> bubbles = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
index c58ab31c4561..26a9773f9bb8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
@@ -18,52 +18,56 @@ package com.android.systemui.bubbles
import android.content.Context
import android.graphics.Color
import android.graphics.Rect
-import android.util.AttributeSet
-import android.view.Gravity
+import android.view.LayoutInflater
import android.view.View
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.Interpolators
+import com.android.systemui.Prefs
+import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION
import com.android.systemui.R
/**
- * Educational view to highlight the manage button that allows a user to configure the settings
+ * User education view to highlight the manage button that allows a user to configure the settings
* for the bubble. Shown only the first time a user expands a bubble.
*/
-class ManageEducationView @JvmOverloads constructor(
- context: Context?,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0,
- defStyleRes: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+class ManageEducationView constructor(context: Context) : LinearLayout(context) {
+
+ private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleManageEducationView"
+ else BubbleDebugConfig.TAG_BUBBLES
+
+ private val ANIMATE_DURATION : Long = 200
+ private val ANIMATE_DURATION_SHORT : Long = 40
private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
private val manageButton by lazy { findViewById<Button>(R.id.manage) }
private val gotItButton by lazy { findViewById<Button>(R.id.got_it) }
private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) }
private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) }
- private var isInflated = false
+
+ private var isHiding = false
init {
- this.visibility = View.GONE
- this.elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
- this.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+ LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this);
+ visibility = View.GONE
+ elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
+
+ // BubbleStackView forces LTR by default
+ // since most of Bubble UI direction depends on positioning by the user.
+ // This view actually lays out differently in RTL, so we set layout LOCALE here.
+ layoutDirection = View.LAYOUT_DIRECTION_LOCALE
}
- override fun setLayoutDirection(direction: Int) {
- super.setLayoutDirection(direction)
- // setLayoutDirection runs before onFinishInflate
- // so skip if views haven't inflated; otherwise we'll get NPEs
- if (!isInflated) return
- setDirection()
+ override fun setLayoutDirection(layoutDirection: Int) {
+ super.setLayoutDirection(layoutDirection)
+ setDrawableDirection()
}
override fun onFinishInflate() {
super.onFinishInflate()
- isInflated = true
- setDirection()
+ layoutDirection = resources.configuration.layoutDirection
setTextColor()
}
@@ -78,29 +82,35 @@ class ManageEducationView @JvmOverloads constructor(
descTextView.setTextColor(textColor)
}
- fun setDirection() {
+ private fun setDrawableDirection() {
manageView.setBackgroundResource(
if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL)
R.drawable.bubble_stack_user_education_bg_rtl
else R.drawable.bubble_stack_user_education_bg)
- titleTextView.gravity = Gravity.START
- descTextView.gravity = Gravity.START
}
- fun show(expandedView: BubbleExpandedView, rect : Rect, hideMenu: Runnable) {
+ /**
+ * If necessary, toggles the user education view for the manage button. This is shown when the
+ * bubble stack is expanded for the first time.
+ *
+ * @param show whether the user education view should show or not.
+ */
+ fun show(expandedView: BubbleExpandedView, rect : Rect) {
+ if (visibility == VISIBLE) return
+
alpha = 0f
visibility = View.VISIBLE
post {
expandedView.getManageButtonBoundsOnScreen(rect)
- with(hideMenu) {
- manageButton
- .setOnClickListener {
- expandedView.findViewById<View>(R.id.settings_button).performClick()
- this.run()
- }
- gotItButton.setOnClickListener { this.run() }
- setOnClickListener { this.run() }
- }
+
+ manageButton
+ .setOnClickListener {
+ expandedView.findViewById<View>(R.id.settings_button).performClick()
+ hide(true /* isStackExpanding */)
+ }
+ gotItButton.setOnClickListener { hide(true /* isStackExpanding */) }
+ setOnClickListener { hide(true /* isStackExpanding */) }
+
with(manageView) {
translationX = 0f
val inset = resources.getDimensionPixelSize(
@@ -109,9 +119,27 @@ class ManageEducationView @JvmOverloads constructor(
}
bringToFront()
animate()
- .setDuration(BubbleStackView.ANIMATE_STACK_USER_EDUCATION_DURATION.toLong())
+ .setDuration(ANIMATE_DURATION)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(1f)
}
+ setShouldShow(false)
+ }
+
+ fun hide(isStackExpanding: Boolean) {
+ if (visibility != VISIBLE || isHiding) return
+
+ animate()
+ .withStartAction { isHiding = true }
+ .alpha(0f)
+ .setDuration(if (isStackExpanding) ANIMATE_DURATION_SHORT else ANIMATE_DURATION)
+ .withEndAction {
+ isHiding = false
+ visibility = GONE
+ };
+ }
+
+ private fun setShouldShow(shouldShow: Boolean) {
+ Prefs.putBoolean(context, HAS_SEEN_BUBBLES_MANAGE_EDUCATION, !shouldShow)
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
new file mode 100644
index 000000000000..3e4c729d8315
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
@@ -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.systemui.bubbles
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.PointF
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.internal.util.ContrastColorUtil
+import com.android.systemui.Interpolators
+import com.android.systemui.Prefs
+import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION
+import com.android.systemui.R
+
+/**
+ * User education view to highlight the collapsed stack of bubbles.
+ * Shown only the first time a user taps the stack.
+ */
+class StackEducationView constructor(context: Context) : LinearLayout(context){
+
+ private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView"
+ else BubbleDebugConfig.TAG_BUBBLES
+
+ private val ANIMATE_DURATION : Long = 200
+ private val ANIMATE_DURATION_SHORT : Long = 40
+
+ private val view by lazy { findViewById<View>(R.id.stack_education_layout) }
+ private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) }
+ private val descTextView by lazy { findViewById<TextView>(R.id.stack_education_description) }
+
+ private var isHiding = false
+
+ init {
+ LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this);
+
+ visibility = View.GONE
+ elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
+
+ // BubbleStackView forces LTR by default
+ // since most of Bubble UI direction depends on positioning by the user.
+ // This view actually lays out differently in RTL, so we set layout LOCALE here.
+ layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+ }
+
+ override fun setLayoutDirection(layoutDirection: Int) {
+ super.setLayoutDirection(layoutDirection)
+ setDrawableDirection()
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ layoutDirection = resources.configuration.layoutDirection
+ setTextColor()
+ }
+
+ private fun setTextColor() {
+ val ta = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
+ android.R.attr.textColorPrimaryInverse))
+ val bgColor = ta.getColor(0 /* index */, Color.BLACK)
+ var textColor = ta.getColor(1 /* index */, Color.WHITE)
+ ta.recycle()
+ textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true)
+ titleTextView.setTextColor(textColor)
+ descTextView.setTextColor(textColor)
+ }
+
+ private fun setDrawableDirection() {
+ view.setBackgroundResource(
+ if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR)
+ R.drawable.bubble_stack_user_education_bg
+ else R.drawable.bubble_stack_user_education_bg_rtl)
+ }
+
+ /**
+ * If necessary, shows the user education view for the bubble stack. This appears the first
+ * time a user taps on a bubble.
+ *
+ * @return true if user education was shown, false otherwise.
+ */
+ fun show(stackPosition: PointF) : Boolean{
+ if (visibility == VISIBLE) return false
+
+ setAlpha(0f)
+ setVisibility(View.VISIBLE)
+ post {
+ with(view) {
+ val bubbleSize = context.resources.getDimensionPixelSize(
+ R.dimen.individual_bubble_size)
+ translationY = stackPosition.y + bubbleSize / 2 - getHeight() / 2
+ }
+ animate()
+ .setDuration(ANIMATE_DURATION)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .alpha(1f)
+ }
+ setShouldShow(false)
+ return true
+ }
+
+ /**
+ * If necessary, hides the stack education view.
+ *
+ * @param fromExpansion if true this indicates the hide is happening due to the bubble being
+ * expanded, false if due to a touch outside of the bubble stack.
+ */
+ fun hide(fromExpansion: Boolean) {
+ if (visibility != VISIBLE || isHiding) return
+
+ animate()
+ .alpha(0f)
+ .setDuration(if (fromExpansion) ANIMATE_DURATION_SHORT else ANIMATE_DURATION)
+ .withEndAction { visibility = GONE }
+ }
+
+ private fun setShouldShow(shouldShow: Boolean) {
+ Prefs.putBoolean(context, HAS_SEEN_BUBBLES_EDUCATION, !shouldShow)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
index 1bda841d4a63..d930c98cabe1 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
@@ -87,6 +87,10 @@ class ControlsFavoritePersistenceWrapper(
* @param list a list of favorite controls. The list will be stored in the same order.
*/
fun storeFavorites(structures: List<StructureInfo>) {
+ if (structures.isEmpty() && !file.exists()) {
+ // Do not create a new file to store nothing
+ return
+ }
executor.execute {
Log.d(TAG, "Saving data to file: $file")
val atomicFile = AtomicFile(file)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
index c95e81cd114b..596e440c3f4a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
@@ -66,12 +66,6 @@ public abstract class DefaultServiceBinder {
@ClassKey(SystemUIAuxiliaryDumpService.class)
public abstract Service bindSystemUIAuxiliaryDumpService(SystemUIAuxiliaryDumpService service);
- /** */
- @Binds
- @IntoMap
- @ClassKey(TakeScreenshotService.class)
- public abstract Service bindTakeScreenshotService(TakeScreenshotService service);
-
/** Inject into RecordingService */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 5a7772320139..2fc5cba35616 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -321,5 +321,4 @@ public class DependencyProvider {
public ModeSwitchesController providesModeSwitchesController(Context context) {
return new ModeSwitchesController(context);
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 7281faf1a2c4..b606201cc803 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -22,7 +22,6 @@ import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
-import com.android.systemui.onehanded.dagger.OneHandedModule;
import com.android.systemui.pip.phone.dagger.PipModule;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.InjectionInflationController;
@@ -37,7 +36,6 @@ import dagger.Subcomponent;
DefaultComponentBinder.class,
DependencyProvider.class,
DependencyBinder.class,
- OneHandedModule.class,
PipModule.class,
SystemServicesModule.class,
SystemUIBinder.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 7fe9faf5258c..e7d2f125935a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -41,6 +41,7 @@ import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.DisplayManager;
+import android.hardware.face.FaceManager;
import android.media.AudioManager;
import android.media.MediaRouter2Manager;
import android.media.session.MediaSessionManager;
@@ -168,6 +169,14 @@ public class SystemServicesModule {
@Provides
@SysUISingleton
+ @Nullable
+ static FaceManager provideFaceManager(Context context) {
+ return context.getSystemService(FaceManager.class);
+
+ }
+
+ @Provides
+ @SysUISingleton
static IPackageManager provideIPackageManager() {
return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index abfc2ab1c6e8..d0e45432c13b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -16,22 +16,16 @@
package com.android.systemui.dagger;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.pm.PackageManager;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.doze.dagger.DozeComponent;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.Recents;
+import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.settings.dagger.SettingsModule;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
@@ -41,12 +35,10 @@ import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
-import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.ConcurrencyModule;
-import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.SensorModule;
import com.android.systemui.util.settings.SettingsUtilModule;
import com.android.systemui.util.time.SystemClock;
@@ -67,6 +59,7 @@ import dagger.Provides;
DemoModeModule.class,
LogModule.class,
PeopleHubModule.class,
+ ScreenshotModule.class,
SensorModule.class,
SettingsModule.class,
SettingsUtilModule.class
@@ -87,22 +80,6 @@ public abstract class SystemUIModule {
public abstract ContextComponentHelper bindComponentHelper(
ContextComponentResolver componentHelper);
- @SysUISingleton
- @Provides
- @Nullable
- static KeyguardLiftController provideKeyguardLiftController(
- Context context,
- StatusBarStateController statusBarStateController,
- AsyncSensorManager asyncSensorManager,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- DumpManager dumpManager) {
- if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
- return null;
- }
- return new KeyguardLiftController(statusBarStateController, asyncSensorManager,
- keyguardUpdateMonitor, dumpManager);
- }
-
/** */
@Binds
public abstract NotificationRowBinder bindNotificationRowBinder(
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 1a1cc072c6bf..19b0ea1db04e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -33,9 +33,6 @@ import java.io.PrintWriter;
import javax.inject.Inject;
-import dagger.Reusable;
-
-@Reusable // Don't create multiple DozeServices.
public class DozeService extends DreamService
implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> {
private static final String TAG = "DozeService";
@@ -60,7 +57,7 @@ public class DozeService extends DreamService
setWindowless(true);
mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */);
- DozeComponent dozeComponent = mDozeComponentBuilder.build();
+ DozeComponent dozeComponent = mDozeComponentBuilder.build(this);
mDozeMachine = dozeComponent.getDozeMachine();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java
index 247285434df9..05050f905e60 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java
@@ -19,6 +19,7 @@ package com.android.systemui.doze.dagger;
import com.android.systemui.doze.DozeMachine;
import com.android.systemui.doze.DozeService;
+import dagger.BindsInstance;
import dagger.Subcomponent;
/**
@@ -30,7 +31,7 @@ public interface DozeComponent {
/** Simple Builder for {@link DozeComponent}. */
@Subcomponent.Factory
interface Builder {
- DozeComponent build();
+ DozeComponent build(@BindsInstance DozeMachine.Service dozeMachineService);
}
/** Supply a {@link DozeMachine}. */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
index a12e280fcca6..04f7c368fdc4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -33,7 +33,6 @@ import com.android.systemui.doze.DozeScreenBrightness;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.doze.DozeScreenStatePreventingAdapter;
import com.android.systemui.doze.DozeSensors;
-import com.android.systemui.doze.DozeService;
import com.android.systemui.doze.DozeSuspendScreenStatePreventingAdapter;
import com.android.systemui.doze.DozeTriggers;
import com.android.systemui.doze.DozeUi;
@@ -52,9 +51,9 @@ public abstract class DozeModule {
@Provides
@DozeScope
@WrappedService
- static DozeMachine.Service providesWrappedService(DozeService dozeService, DozeHost dozeHost,
- DozeParameters dozeParameters) {
- DozeMachine.Service wrappedService = dozeService;
+ static DozeMachine.Service providesWrappedService(DozeMachine.Service dozeMachineService,
+ DozeHost dozeHost, DozeParameters dozeParameters) {
+ DozeMachine.Service wrappedService = dozeMachineService;
wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost);
wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded(
wrappedService, dozeParameters);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
new file mode 100644
index 000000000000..9dcc3bb6fbdc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
@@ -0,0 +1,193 @@
+/*
+ * 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.keyguard
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.content.res.Resources
+import android.database.ContentObserver
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.hardware.biometrics.BiometricSourceType
+import android.os.Handler
+import android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT
+import android.util.MathUtils
+import android.view.View
+import android.view.WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
+import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+import com.android.internal.annotations.VisibleForTesting
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SystemSettings
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.Float.max
+import java.util.concurrent.TimeUnit
+
+val DEFAULT_ANIMATION_DURATION = TimeUnit.SECONDS.toMillis(4)
+val MAX_SCREEN_BRIGHTNESS = 100 // 0..100
+val MAX_SCRIM_OPACTY = 50 // 0..100
+val DEFAULT_USE_FACE_WALLPAPER = false
+
+/**
+ * This class is responsible for ramping up the display brightness (and white overlay) in order
+ * to mitigate low light conditions when running face auth without an IR camera.
+ */
+@SysUISingleton
+open class FaceAuthScreenBrightnessController(
+ private val notificationShadeWindowController: NotificationShadeWindowController,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val resources: Resources,
+ private val globalSettings: GlobalSettings,
+ private val systemSettings: SystemSettings,
+ private val mainHandler: Handler,
+ private val dumpManager: DumpManager
+) : Dumpable {
+
+ private var userDefinedBrightness: Float = 1f
+ @VisibleForTesting
+ var useFaceAuthWallpaper = globalSettings
+ .getInt("sysui.use_face_auth_wallpaper", if (DEFAULT_USE_FACE_WALLPAPER) 1 else 0) == 1
+ private val brightnessAnimationDuration = globalSettings
+ .getLong("sysui.face_brightness_anim_duration", DEFAULT_ANIMATION_DURATION)
+ private val maxScreenBrightness = globalSettings
+ .getInt("sysui.face_max_brightness", MAX_SCREEN_BRIGHTNESS) / 100f
+ private val maxScrimOpacity = globalSettings
+ .getInt("sysui.face_max_scrim_opacity", MAX_SCRIM_OPACTY) / 100f
+ private val keyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() {
+ override fun onBiometricRunningStateChanged(
+ running: Boolean,
+ biometricSourceType: BiometricSourceType?
+ ) {
+ if (biometricSourceType != BiometricSourceType.FACE) {
+ return
+ }
+ // TODO enable only when receiving a low-light error
+ overridingBrightness = running
+ }
+ }
+ private lateinit var whiteOverlay: View
+ private var brightnessAnimator: ValueAnimator? = null
+ private var overridingBrightness = false
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ brightnessAnimator?.cancel()
+
+ if (!value) {
+ notificationShadeWindowController.setFaceAuthDisplayBrightness(BRIGHTNESS_OVERRIDE_NONE)
+ if (whiteOverlay.alpha > 0) {
+ brightnessAnimator = createAnimator(whiteOverlay.alpha, 0f).apply {
+ duration = 200
+ addUpdateListener {
+ whiteOverlay.alpha = it.animatedValue as Float
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ whiteOverlay.visibility = View.INVISIBLE
+ brightnessAnimator = null
+ }
+ })
+ start()
+ }
+ }
+ return
+ }
+
+ val targetBrightness = max(maxScreenBrightness, userDefinedBrightness)
+ whiteOverlay.visibility = View.VISIBLE
+ brightnessAnimator = createAnimator(0f, 1f).apply {
+ duration = brightnessAnimationDuration
+ addUpdateListener {
+ val progress = it.animatedValue as Float
+ val brightnessProgress = MathUtils.constrainedMap(
+ userDefinedBrightness, targetBrightness, 0f, 0.5f, progress)
+ val scrimProgress = MathUtils.constrainedMap(
+ 0f, maxScrimOpacity, 0.5f, 1f, progress)
+ notificationShadeWindowController.setFaceAuthDisplayBrightness(brightnessProgress)
+ whiteOverlay.alpha = scrimProgress
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ brightnessAnimator = null
+ }
+ })
+ start()
+ }
+ }
+
+ @VisibleForTesting
+ open fun createAnimator(start: Float, end: Float) = ValueAnimator.ofFloat(start, end)
+
+ /**
+ * Returns a bitmap that should be used by the lock screen as a wallpaper, if face auth requires
+ * a secure wallpaper.
+ */
+ var faceAuthWallpaper: Bitmap? = null
+ get() {
+ val user = KeyguardUpdateMonitor.getCurrentUser()
+ if (useFaceAuthWallpaper && keyguardUpdateMonitor.isFaceAuthEnabledForUser(user)) {
+ val options = BitmapFactory.Options().apply {
+ inScaled = false
+ }
+ return BitmapFactory.decodeResource(resources, R.drawable.face_auth_wallpaper, options)
+ }
+ return null
+ }
+ private set
+
+ fun attach(overlayView: View) {
+ whiteOverlay = overlayView
+ whiteOverlay.focusable = FLAG_NOT_FOCUSABLE
+ whiteOverlay.background = ColorDrawable(Color.WHITE)
+ whiteOverlay.isEnabled = false
+ whiteOverlay.alpha = 0f
+ whiteOverlay.visibility = View.INVISIBLE
+
+ dumpManager.registerDumpable(this.javaClass.name, this)
+ keyguardUpdateMonitor.registerCallback(keyguardUpdateCallback)
+ systemSettings.registerContentObserver(SCREEN_BRIGHTNESS_FLOAT,
+ object : ContentObserver(mainHandler) {
+ override fun onChange(selfChange: Boolean) {
+ userDefinedBrightness = systemSettings.getFloat(SCREEN_BRIGHTNESS_FLOAT)
+ }
+ })
+ userDefinedBrightness = systemSettings.getFloat(SCREEN_BRIGHTNESS_FLOAT)
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.apply {
+ println("overridingBrightness: $overridingBrightness")
+ println("useFaceAuthWallpaper: $useFaceAuthWallpaper")
+ println("brightnessAnimator: $brightnessAnimator")
+ println("brightnessAnimationDuration: $brightnessAnimationDuration")
+ println("maxScreenBrightness: $maxScreenBrightness")
+ println("userDefinedBrightness: $userDefinedBrightness")
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 7c5dcd8960c4..c9164f0c4459 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -16,8 +16,13 @@
package com.android.systemui.keyguard.dagger;
+import android.annotation.Nullable;
import android.app.trust.TrustManager;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.face.FaceManager;
+import android.os.Handler;
import android.os.PowerManager;
import com.android.internal.widget.LockPatternUtils;
@@ -25,16 +30,25 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardViewController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.settings.SystemSettings;
+import java.util.Optional;
import java.util.concurrent.Executor;
import dagger.Lazy;
@@ -82,4 +96,50 @@ public class KeyguardModule {
navigationModeController,
injectionInflationController);
}
+
+ @SysUISingleton
+ @Provides
+ @Nullable
+ static KeyguardLiftController provideKeyguardLiftController(
+ Context context,
+ StatusBarStateController statusBarStateController,
+ AsyncSensorManager asyncSensorManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DumpManager dumpManager) {
+ if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ return null;
+ }
+ return new KeyguardLiftController(statusBarStateController, asyncSensorManager,
+ keyguardUpdateMonitor, dumpManager);
+ }
+
+ @SysUISingleton
+ @Provides
+ static Optional<FaceAuthScreenBrightnessController> provideFaceAuthScreenBrightnessController(
+ Context context,
+ NotificationShadeWindowController notificationShadeWindowController,
+ @Main Resources resources,
+ Handler handler,
+ @Nullable FaceManager faceManager,
+ PackageManager packageManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ GlobalSettings globalSetting,
+ SystemSettings systemSettings,
+ DumpManager dumpManager) {
+ if (faceManager == null || !packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ return Optional.empty();
+ }
+
+ // Cameras that support "self illumination," via IR for example, don't need low light
+ // environment mitigation.
+ boolean needsLowLightMitigation = faceManager.getSensorProperties().stream()
+ .anyMatch((properties) -> !properties.supportsSelfIllumination);
+ if (!needsLowLightMitigation) {
+ return Optional.empty();
+ }
+
+ return Optional.of(new FaceAuthScreenBrightnessController(
+ notificationShadeWindowController, keyguardUpdateMonitor, resources,
+ globalSetting, systemSettings, handler, dumpManager));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index fdbff98ea831..6bd5274fa331 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -54,32 +54,35 @@ class MediaTimeoutListener @Inject constructor(
if (mediaListeners.containsKey(key)) {
return
}
- // Having an old key means that we're migrating from/to resumption. We should invalidate
- // the old listener and create a new one.
+ // Having an old key means that we're migrating from/to resumption. We should update
+ // the old listener to make sure that events will be dispatched to the new location.
val migrating = oldKey != null && key != oldKey
var wasPlaying = false
if (migrating) {
- if (mediaListeners.containsKey(oldKey)) {
- val oldListener = mediaListeners.remove(oldKey)
- wasPlaying = oldListener?.playing ?: false
- oldListener?.destroy()
+ val reusedListener = mediaListeners.remove(oldKey)
+ if (reusedListener != null) {
+ wasPlaying = reusedListener.playing ?: false
if (DEBUG) Log.d(TAG, "migrating key $oldKey to $key, for resumption")
+ reusedListener.mediaData = data
+ reusedListener.key = key
+ mediaListeners[key] = reusedListener
+ if (wasPlaying != reusedListener.playing) {
+ // If a player becomes active because of a migration, we'll need to broadcast
+ // its state. Doing it now would lead to reentrant callbacks, so let's wait
+ // until we're done.
+ mainExecutor.execute {
+ if (mediaListeners[key]?.playing == true) {
+ if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key")
+ timeoutCallback.invoke(key, false /* timedOut */)
+ }
+ }
+ }
+ return
} else {
Log.w(TAG, "Old key $oldKey for player $key doesn't exist. Continuing...")
}
}
mediaListeners[key] = PlaybackStateListener(key, data)
-
- // If a player becomes active because of a migration, we'll need to broadcast its state.
- // Doing it now would lead to reentrant callbacks, so let's wait until we're done.
- if (migrating && mediaListeners[key]?.playing != wasPlaying) {
- mainExecutor.execute {
- if (mediaListeners[key]?.playing == true) {
- if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key")
- timeoutCallback.invoke(key, false /* timedOut */)
- }
- }
- }
}
override fun onMediaDataRemoved(key: String) {
@@ -91,26 +94,34 @@ class MediaTimeoutListener @Inject constructor(
}
private inner class PlaybackStateListener(
- private val key: String,
+ var key: String,
data: MediaData
) : MediaController.Callback() {
var timedOut = false
var playing: Boolean? = null
+ var mediaData: MediaData = data
+ set(value) {
+ mediaController?.unregisterCallback(this)
+ field = value
+ mediaController = if (field.token != null) {
+ mediaControllerFactory.create(field.token)
+ } else {
+ null
+ }
+ mediaController?.registerCallback(this)
+ // Let's register the cancellations, but not dispatch events now.
+ // Timeouts didn't happen yet and reentrant events are troublesome.
+ processState(mediaController?.playbackState, dispatchEvents = false)
+ }
+
// Resume controls may have null token
- private val mediaController = if (data.token != null) {
- mediaControllerFactory.create(data.token)
- } else {
- null
- }
+ private var mediaController: MediaController? = null
private var cancellation: Runnable? = null
init {
- mediaController?.registerCallback(this)
- // Let's register the cancellations, but not dispatch events now.
- // Timeouts didn't happen yet and reentrant events are troublesome.
- processState(mediaController?.playbackState, dispatchEvents = false)
+ mediaData = data
}
fun destroy() {
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java
index 90e7e12b2b47..bb59449d114d 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java
@@ -49,7 +49,7 @@ import javax.inject.Inject;
* Manages and manipulates the one handed states, transitions, and gesture for phones.
*/
@SysUISingleton
-public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
+public class OneHandedController implements Dumpable {
private static final String TAG = "OneHandedManager";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
"persist.debug.one_handed_offset_percentage";
@@ -106,7 +106,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
* Constructor of OneHandedManager
*/
@Inject
- public OneHandedManagerImpl(Context context,
+ public OneHandedController(Context context,
CommandQueue commandQueue,
DisplayController displayController,
NavigationModeController navigationModeController,
@@ -137,7 +137,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
*/
// TODO(b/161980408): Should remove extra constructor.
@VisibleForTesting
- OneHandedManagerImpl(Context context,
+ OneHandedController(Context context,
CommandQueue commandQueue,
DisplayController displayController,
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
@@ -194,7 +194,6 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
/**
* Enters one handed mode.
*/
- @Override
public void startOneHanded() {
if (!mDisplayAreaOrganizer.isInOneHanded()) {
final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction);
@@ -206,7 +205,6 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
/**
* Exits one handed mode.
*/
- @Override
public void stopOneHanded() {
if (mDisplayAreaOrganizer.isInOneHanded()) {
mDisplayAreaOrganizer.scheduleOffset(0, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
index 1420811b9b30..f3be699ab821 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
@@ -103,7 +103,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
}
/**
- * Notified by {@link OneHandedManager}, when user update settings of Enabled or Disabled
+ * Notified by {@link OneHandedController}, when user update settings of Enabled or Disabled
*
* @param isEnabled is one handed settings enabled or not
*/
@@ -264,7 +264,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
}
/**
- * The touch(gesture) events to notify {@link OneHandedManager} start or stop one handed
+ * The touch(gesture) events to notify {@link OneHandedController} start or stop one handed
*/
public interface OneHandedGestureEventCallback {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
index 0a7eb1bdada0..8265da6a5f14 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
@@ -63,7 +63,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpa
}
/**
- * Notified by {@link OneHandedManagerImpl}, when user update settings of Enabled or Disabled
+ * Notified by {@link OneHandedController}, when user update settings of Enabled or Disabled
*
* @param isEnabled is one handed settings enabled or not
*/
@@ -166,7 +166,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpa
}
/**
- * The touch(gesture) events to notify {@link OneHandedManager} start or stop one handed
+ * The touch(gesture) events to notify {@link OneHandedController} start or stop one handed
*/
public interface OneHandedTouchEventCallback {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
index 8ef9b092bc00..0354c727c92c 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
@@ -158,9 +158,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
mDisplaySize.x, mTutorialAreaHeight, 0, 0,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
lp.gravity = Gravity.TOP | Gravity.LEFT;
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
index cebcd4ceabe9..3348a06d5cac 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
@@ -59,7 +59,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
"com.android.internal.systemui.onehanded.gestural";
private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
- private final OneHandedManagerImpl mOneHandedManager;
+ private final OneHandedController mOneHandedController;
private final CommandQueue mCommandQueue;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private final IOverlayManager mOverlayManager;
@@ -74,8 +74,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
OneHandedEvents.writeEvent(enabled
? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON
: OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF);
- if (mOneHandedManager != null) {
- mOneHandedManager.setOneHandedEnabled(enabled);
+ if (mOneHandedController != null) {
+ mOneHandedController.setOneHandedEnabled(enabled);
}
// Also checks swipe to notification settings since they all need gesture overlay.
@@ -125,8 +125,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON
: OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF);
- if (mOneHandedManager != null) {
- mOneHandedManager.setTaskChangeToExit(enabled);
+ if (mOneHandedController != null) {
+ mOneHandedController.setTaskChangeToExit(enabled);
}
}
};
@@ -138,8 +138,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
final boolean enabled =
OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver());
- if (mOneHandedManager != null) {
- mOneHandedManager.setSwipeToNotificationEnabled(enabled);
+ if (mOneHandedController != null) {
+ mOneHandedController.setSwipeToNotificationEnabled(enabled);
}
// Also checks one handed mode settings since they all need gesture overlay.
@@ -152,14 +152,14 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
@Inject
public OneHandedUI(Context context,
CommandQueue commandQueue,
- OneHandedManagerImpl oneHandedManager,
+ OneHandedController oneHandedController,
ScreenLifecycle screenLifecycle) {
super(context);
if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off");
mCommandQueue = null;
- mOneHandedManager = null;
+ mOneHandedController = null;
mOverlayManager = null;
mTimeoutHandler = null;
mScreenLifecycle = null;
@@ -167,7 +167,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
}
mCommandQueue = commandQueue;
- mOneHandedManager = oneHandedManager;
+ mOneHandedController = oneHandedController;
mTimeoutHandler = OneHandedTimeoutHandler.get();
mScreenLifecycle = screenLifecycle;
mOverlayManager = IOverlayManager.Stub.asInterface(
@@ -260,13 +260,13 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
}
private void updateSettings() {
- mOneHandedManager.setOneHandedEnabled(OneHandedSettingsUtil
+ mOneHandedController.setOneHandedEnabled(OneHandedSettingsUtil
.getSettingsOneHandedModeEnabled(mContext.getContentResolver()));
mTimeoutHandler.setTimeout(OneHandedSettingsUtil
.getSettingsOneHandedModeTimeout(mContext.getContentResolver()));
- mOneHandedManager.setTaskChangeToExit(OneHandedSettingsUtil
+ mOneHandedController.setTaskChangeToExit(OneHandedSettingsUtil
.getSettingsTapsAppToExit(mContext.getContentResolver()));
- mOneHandedManager.setSwipeToNotificationEnabled(OneHandedSettingsUtil
+ mOneHandedController.setSwipeToNotificationEnabled(OneHandedSettingsUtil
.getSettingsSwipeToNotificationEnabled(mContext.getContentResolver()));
}
@@ -295,7 +295,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
* Trigger one handed more
*/
public void startOneHanded() {
- mOneHandedManager.startOneHanded();
+ mOneHandedController.startOneHanded();
OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN);
}
@@ -303,7 +303,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
* Dismiss one handed more
*/
public void stopOneHanded() {
- mOneHandedManager.stopOneHanded();
+ mOneHandedController.stopOneHanded();
OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
}
@@ -314,8 +314,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
final String innerPrefix = " ";
pw.println(TAG + "one handed states: ");
- if (mOneHandedManager != null) {
- ((OneHandedManagerImpl) mOneHandedManager).dump(fd, pw, args);
+ if (mOneHandedController != null) {
+ mOneHandedController.dump(fd, pw, args);
}
if (mTimeoutHandler != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index 4931388fe362..fd8ca8044acf 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -31,8 +31,6 @@ import com.android.systemui.Interpolators;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import javax.inject.Inject;
-
/**
* Controller class of PiP animations (both from and to PiP mode).
*/
@@ -88,7 +86,6 @@ public class PipAnimationController {
return handler;
});
- @Inject
PipAnimationController(PipSurfaceTransactionHelper helper) {
mSurfaceTransactionHelper = helper;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index d6aa61b0c767..b464e8adea59 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -40,13 +40,10 @@ import android.view.Gravity;
import android.window.WindowContainerTransaction;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import java.io.PrintWriter;
-import javax.inject.Inject;
-
/**
* Handles bounds calculation for PIP on Phone and other form factors, it keeps tracking variant
* state changes originated from Window Manager and is the source of truth for PiP window bounds.
@@ -57,10 +54,8 @@ public class PipBoundsHandler {
private static final String TAG = PipBoundsHandler.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
- private final Context mContext;
private final PipSnapAlgorithm mSnapAlgorithm;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
- private final DisplayController mDisplayController;
private DisplayLayout mDisplayLayout;
private ComponentName mLastPipComponentName;
@@ -82,25 +77,10 @@ public class PipBoundsHandler {
private boolean mIsShelfShowing;
private int mShelfHeight;
- private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
- new DisplayController.OnDisplaysChangedListener() {
- @Override
- public void onDisplayAdded(int displayId) {
- if (displayId == mContext.getDisplayId()) {
- mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId));
- }
- }
- };
-
- @Inject
- public PipBoundsHandler(Context context, PipSnapAlgorithm pipSnapAlgorithm,
- DisplayController displayController) {
- mContext = context;
- mSnapAlgorithm = pipSnapAlgorithm;
+ public PipBoundsHandler(Context context) {
+ mSnapAlgorithm = new PipSnapAlgorithm(context);
mDisplayLayout = new DisplayLayout();
- mDisplayController = displayController;
- mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
- reloadResources();
+ reloadResources(context);
// Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
// resources as it would clobber mAspectRatio when entering PiP from fullscreen which
// triggers a configuration change and the resources to be reloaded.
@@ -110,8 +90,8 @@ public class PipBoundsHandler {
/**
* TODO: move the resources to SysUI package.
*/
- private void reloadResources() {
- final Resources res = mContext.getResources();
+ private void reloadResources(Context context) {
+ final Resources res = context.getResources();
mDefaultAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
mDefaultStackGravity = res.getInteger(
@@ -133,6 +113,19 @@ public class PipBoundsHandler {
com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
}
+ /**
+ * Sets or update latest {@link DisplayLayout} when new display added or rotation callbacks
+ * from {@link DisplayController.OnDisplaysChangedListener}
+ * @param newDisplayLayout latest {@link DisplayLayout}
+ */
+ public void setDisplayLayout(DisplayLayout newDisplayLayout) {
+ mDisplayLayout.set(newDisplayLayout);
+ }
+
+ /**
+ * Update the Min edge size for {@link PipSnapAlgorithm} to calculate corresponding bounds
+ * @param minEdgeSize
+ */
public void setMinEdgeSize(int minEdgeSize) {
mCurrentMinSize = minEdgeSize;
}
@@ -217,6 +210,14 @@ public class PipBoundsHandler {
return mReentrySnapFraction != INVALID_SNAP_FRACTION;
}
+ /**
+ * The {@link PipSnapAlgorithm} is couple on display bounds
+ * @return {@link PipSnapAlgorithm}.
+ */
+ public PipSnapAlgorithm getSnapAlgorithm() {
+ return mSnapAlgorithm;
+ }
+
public Rect getDisplayBounds() {
return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
}
@@ -237,8 +238,8 @@ public class PipBoundsHandler {
/**
* Responds to IPinnedStackListener on configuration change.
*/
- public void onConfigurationChanged() {
- reloadResources();
+ public void onConfigurationChanged(Context context) {
+ reloadResources(context);
}
/**
@@ -300,10 +301,10 @@ public class PipBoundsHandler {
* aren't in PIP because the rotation layout is used to calculate the proper insets for the
* next enter animation into PIP.
*/
- public void onDisplayRotationChangedNotInPip(int toRotation) {
+ public void onDisplayRotationChangedNotInPip(Context context, int toRotation) {
// Update the display layout, note that we have to do this on every rotation even if we
// aren't in PIP since we need to update the display layout to get the right resources
- mDisplayLayout.rotateTo(mContext.getResources(), toRotation);
+ mDisplayLayout.rotateTo(context.getResources(), toRotation);
// Populate the new {@link #mDisplayInfo}.
// The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
@@ -319,7 +320,8 @@ public class PipBoundsHandler {
*
* @return {@code true} if internal {@link DisplayInfo} is rotated, {@code false} otherwise.
*/
- public boolean onDisplayRotationChanged(Rect outBounds, Rect oldBounds, Rect outInsetBounds,
+ public boolean onDisplayRotationChanged(Context context, Rect outBounds, Rect oldBounds,
+ Rect outInsetBounds,
int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) {
// Bail early if the event is not sent to current {@link #mDisplayInfo}
if ((displayId != mDisplayInfo.displayId) || (fromRotation == toRotation)) {
@@ -342,7 +344,7 @@ public class PipBoundsHandler {
final float snapFraction = getSnapFraction(postChangeStackBounds);
// Update the display layout
- mDisplayLayout.rotateTo(mContext.getResources(), toRotation);
+ mDisplayLayout.rotateTo(context.getResources(), toRotation);
// Populate the new {@link #mDisplayInfo}.
// The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
@@ -546,5 +548,6 @@ public class PipBoundsHandler {
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
+ pw.println(innerPrefix + "mSnapAlgorithm" + mSnapAlgorithm);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
index a9b32d917d85..5d23e4207c33 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
@@ -22,24 +22,18 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.util.Size;
-import javax.inject.Inject;
-
/**
* Calculates the snap targets and the snap position for the PIP given a position and a velocity.
* All bounds are relative to the display top/left.
*/
public class PipSnapAlgorithm {
- private final Context mContext;
-
private final float mDefaultSizePercent;
private final float mMinAspectRatioForMinSize;
private final float mMaxAspectRatioForMinSize;
- @Inject
public PipSnapAlgorithm(Context context) {
Resources res = context.getResources();
- mContext = context;
mDefaultSizePercent = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
mMaxAspectRatioForMinSize = res.getFloat(
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
index e88451ca00b5..3e98169c5b2b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
@@ -27,8 +27,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.wm.shell.R;
-import javax.inject.Inject;
-
/**
* Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition.
*/
@@ -46,7 +44,6 @@ public class PipSurfaceTransactionHelper implements ConfigurationController.Conf
private final RectF mTmpDestinationRectF = new RectF();
private final Rect mTmpDestinationRect = new Rect();
- @Inject
public PipSurfaceTransactionHelper(Context context, ConfigurationController configController) {
final Resources res = context.getResources();
mContext = context;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 0e60c83b8392..1ea58887ddc7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -60,6 +60,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.pip.phone.PipUpdateThread;
import com.android.systemui.stackdivider.Divider;
import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import java.io.PrintWriter;
@@ -70,8 +71,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
-import javax.inject.Inject;
-
/**
* Manages PiP tasks such as resize and offset.
*
@@ -84,7 +83,7 @@ import javax.inject.Inject;
* see also {@link com.android.systemui.pip.phone.PipMotionHelper}.
*/
@SysUISingleton
-public class PipTaskOrganizer extends TaskOrganizer implements
+public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganizer.TaskListener,
DisplayController.OnDisplaysChangedListener {
private static final String TAG = PipTaskOrganizer.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -106,6 +105,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
private final Divider mSplitDivider;
+ protected final ShellTaskOrganizer mTaskOrganizer;
// These callbacks are called on the update thread
private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
@@ -205,23 +205,24 @@ public class PipTaskOrganizer extends TaskOrganizer implements
*/
private boolean mShouldDeferEnteringPip;
- @Inject
public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@Nullable Divider divider,
@NonNull DisplayController displayController,
- @NonNull PipAnimationController pipAnimationController,
- @NonNull PipUiEventLogger pipUiEventLogger) {
+ @NonNull PipUiEventLogger pipUiEventLogger,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer) {
mMainHandler = new Handler(Looper.getMainLooper());
mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
mPipBoundsHandler = boundsHandler;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
mSurfaceTransactionHelper = surfaceTransactionHelper;
- mPipAnimationController = pipAnimationController;
+ mPipAnimationController = new PipAnimationController(mSurfaceTransactionHelper);
mPipUiEventLoggerLogger = pipUiEventLogger;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mSplitDivider = divider;
+ mTaskOrganizer = shellTaskOrganizer;
+ mTaskOrganizer.addListener(this, WINDOWING_MODE_PINNED);
displayController.addDisplayWindowListener(this);
}
@@ -316,7 +317,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
: WINDOWING_MODE_FULLSCREEN);
wct.setBounds(mToken, destinationBounds);
wct.setBoundsChangeTransaction(mToken, tx);
- applySyncTransaction(wct, new WindowContainerTransactionCallback() {
+ mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
@Override
public void onTransactionReady(int id, SurfaceControl.Transaction t) {
t.apply();
@@ -446,7 +447,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
wct.setBounds(mToken, destinationBounds);
wct.scheduleFinishEnterPip(mToken, destinationBounds);
- applySyncTransaction(wct, new WindowContainerTransactionCallback() {
+ mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
@Override
public void onTransactionReady(int id, SurfaceControl.Transaction t) {
t.apply();
@@ -543,11 +544,6 @@ public class PipTaskOrganizer extends TaskOrganizer implements
}
@Override
- public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
- // Do nothing
- }
-
- @Override
public void onFixedRotationStarted(int displayId, int newRotation) {
mShouldDeferEnteringPip = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
index 7ce2028b5f1b..8bcaa8ab5404 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
@@ -22,9 +22,6 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dagger.SysUISingleton;
-import javax.inject.Inject;
-
-
/**
* Helper class that ends PiP log to UiEvent, see also go/uievent
*/
@@ -35,7 +32,6 @@ public class PipUiEventLogger {
private TaskInfo mTaskInfo;
- @Inject
public PipUiEventLogger(UiEventLogger uiEventLogger) {
mUiEventLogger = uiEventLogger;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index facb3966f78c..27cc3d458062 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -46,7 +46,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.BasePipManager;
import com.android.systemui.pip.PipBoundsHandler;
-import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
@@ -56,9 +56,11 @@ import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
@@ -82,15 +84,17 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
private final Rect mTmpNormalBounds = new Rect();
protected final Rect mReentryBounds = new Rect();
- private PipBoundsHandler mPipBoundsHandler;
+ private DisplayController mDisplayController;
private InputConsumerController mInputConsumerController;
+ private PipAppOpsListener mAppOpsListener;
private PipMediaController mMediaController;
private PipTouchHandler mTouchHandler;
private PipTaskOrganizer mPipTaskOrganizer;
- private PipAppOpsListener mAppOpsListener;
+ private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener;
private boolean mIsInFixedRotation;
+ protected PipBoundsHandler mPipBoundsHandler;
protected PipMenuActivityController mMenuController;
/**
@@ -101,15 +105,16 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
if (!mPipTaskOrganizer.isInPip() || mPipTaskOrganizer.isDeferringEnterPipAnimation()) {
// Skip if we aren't in PIP or haven't actually entered PIP yet. We still need to update
// the display layout in the bounds handler in this case.
- mPipBoundsHandler.onDisplayRotationChangedNotInPip(toRotation);
+ mPipBoundsHandler.onDisplayRotationChangedNotInPip(mContext, toRotation);
return;
}
// If there is an animation running (ie. from a shelf offset), then ensure that we calculate
// the bounds for the next orientation using the destination bounds of the animation
// TODO: Techincally this should account for movement animation bounds as well
Rect currentBounds = mPipTaskOrganizer.getCurrentOrAnimatingBounds();
- final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mTmpNormalBounds,
- currentBounds, mTmpInsetBounds, displayId, fromRotation, toRotation, t);
+ final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mContext,
+ mTmpNormalBounds, currentBounds, mTmpInsetBounds, displayId, fromRotation,
+ toRotation, t);
if (changed) {
// If the pip was in the offset zone earlier, adjust the new bounds to the bottom of the
// movement bounds
@@ -135,16 +140,22 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
private DisplayController.OnDisplaysChangedListener mFixedRotationListener =
new DisplayController.OnDisplaysChangedListener() {
- @Override
- public void onFixedRotationStarted(int displayId, int newRotation) {
- mIsInFixedRotation = true;
- }
-
- @Override
- public void onFixedRotationFinished(int displayId) {
- mIsInFixedRotation = false;
- }
- };
+ @Override
+ public void onFixedRotationStarted(int displayId, int newRotation) {
+ mIsInFixedRotation = true;
+ }
+
+ @Override
+ public void onFixedRotationFinished(int displayId) {
+ mIsInFixedRotation = false;
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ mPipBoundsHandler.setDisplayLayout(
+ mDisplayController.getDisplayLayout(displayId));
+ }
+ };
/**
* Handler for system task stack changes.
@@ -228,7 +239,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
@Override
public void onConfigurationChanged() {
- mHandler.post(() -> mPipBoundsHandler.onConfigurationChanged());
+ mHandler.post(() -> mPipBoundsHandler.onConfigurationChanged(mContext));
}
@Override
@@ -256,15 +267,14 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
@Inject
public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
@PipMenuActivityClass Class<?> pipMenuActivityClass,
+ ConfigurationController configController,
+ DeviceConfigProxy deviceConfig,
DisplayController displayController,
+ Divider divider,
FloatingContentCoordinator floatingContentCoordinator,
- DeviceConfigProxy deviceConfig,
- PipBoundsHandler pipBoundsHandler,
- PipSnapAlgorithm pipSnapAlgorithm,
- PipTaskOrganizer pipTaskOrganizer,
SysUiState sysUiState,
- ConfigurationController configController,
- PipUiEventLogger pipUiEventLogger) {
+ PipUiEventLogger pipUiEventLogger,
+ ShellTaskOrganizer shellTaskOrganizer) {
mContext = context;
mActivityManager = ActivityManager.getService();
@@ -276,8 +286,12 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
}
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
- mPipBoundsHandler = pipBoundsHandler;
- mPipTaskOrganizer = pipTaskOrganizer;
+ mDisplayController = displayController;
+ mPipBoundsHandler = new PipBoundsHandler(mContext);
+ mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context, configController);
+ mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler,
+ mPipSurfaceTransactionHelper, divider, mDisplayController, pipUiEventLogger,
+ shellTaskOrganizer);
mPipTaskOrganizer.registerPipTransitionCallback(this);
mInputConsumerController = InputConsumerController.getPipInputConsumer();
mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
@@ -285,8 +299,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
mMediaController, mInputConsumerController);
mTouchHandler = new PipTouchHandler(context, mActivityManager,
mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
- floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState,
- pipUiEventLogger);
+ floatingContentCoordinator, deviceConfig, sysUiState, pipUiEventLogger);
mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
mTouchHandler.getMotionHelper());
displayController.addDisplayChangingController(mRotationController);
@@ -301,7 +314,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
configController.addCallback(mOverlayChangedListener);
try {
- mPipTaskOrganizer.registerOrganizer(WINDOWING_MODE_PINNED);
ActivityManager.StackInfo stackInfo = ActivityTaskManager.getService().getStackInfo(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stackInfo != null) {
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 ecd315b336f2..1b84c1417c51 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -58,7 +58,6 @@ import com.android.systemui.R;
import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.PipBoundsHandler;
-import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
import com.android.systemui.shared.system.InputConsumerController;
@@ -99,7 +98,6 @@ public class PipTouchHandler {
private IPinnedStackController mPinnedStackController;
private final PipMenuActivityController mMenuController;
- private final PipSnapAlgorithm mSnapAlgorithm;
private final AccessibilityManager mAccessibilityManager;
private boolean mShowPipMenuOnAnimationEnd = false;
@@ -216,20 +214,19 @@ public class PipTouchHandler {
PipTaskOrganizer pipTaskOrganizer,
FloatingContentCoordinator floatingContentCoordinator,
DeviceConfigProxy deviceConfig,
- PipSnapAlgorithm pipSnapAlgorithm,
SysUiState sysUiState,
PipUiEventLogger pipUiEventLogger) {
// Initialize the Pip input consumer
mContext = context;
mActivityManager = activityManager;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+ mPipBoundsHandler = pipBoundsHandler;
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mMenuController = menuController;
mMenuController.addListener(new PipMenuListener());
- mSnapAlgorithm = pipSnapAlgorithm;
mGesture = new DefaultPipTouchGesture();
mMotionHelper = new PipMotionHelper(mContext, pipTaskOrganizer, mMenuController,
- mSnapAlgorithm, floatingContentCoordinator);
+ mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator);
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
deviceConfig, pipTaskOrganizer, this::getMovementBounds,
@@ -248,11 +245,10 @@ public class PipTouchHandler {
inputConsumerController.setInputListener(this::handleTouchEvent);
inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
- mPipBoundsHandler = pipBoundsHandler;
mFloatingContentCoordinator = floatingContentCoordinator;
mConnection = new PipAccessibilityInteractionConnection(mContext, mMotionHelper,
- pipTaskOrganizer, pipSnapAlgorithm, this::onAccessibilityShowMenu,
- this::updateMovementBounds, mHandler);
+ pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(),
+ this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler);
mPipUiEventLogger = pipUiEventLogger;
@@ -419,7 +415,8 @@ public class PipTouchHandler {
public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) {
final Rect toMovementBounds = new Rect();
- mSnapAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0);
+ mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds,
+ toMovementBounds, 0);
final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
if ((prevBottom - mBottomOffsetBufferPx) <= curBounds.top) {
outBounds.offsetTo(outBounds.left, toMovementBounds.bottom);
@@ -450,26 +447,26 @@ public class PipTouchHandler {
// Re-calculate the expanded bounds
mNormalBounds.set(normalBounds);
Rect normalMovementBounds = new Rect();
- mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds,
- bottomOffset);
+ mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mNormalBounds, insetBounds,
+ normalMovementBounds, bottomOffset);
if (mMovementBounds.isEmpty()) {
// mMovementBounds is not initialized yet and a clean movement bounds without
// bottom offset shall be used later in this function.
- mSnapAlgorithm.getMovementBounds(curBounds, insetBounds, mMovementBounds,
- 0 /* bottomOffset */);
+ mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds,
+ mMovementBounds, 0 /* bottomOffset */);
}
// Calculate the expanded size
float aspectRatio = (float) normalBounds.width() / normalBounds.height();
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
- Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
+ Size expandedSize = mPipBoundsHandler.getSnapAlgorithm().getSizeForAspectRatio(aspectRatio,
mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
Rect expandedMovementBounds = new Rect();
- mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
- bottomOffset);
+ mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mExpandedBounds, insetBounds,
+ expandedMovementBounds, bottomOffset);
mPipResizeGestureHandler.updateMinSize(mNormalBounds.width(), mNormalBounds.height());
mPipResizeGestureHandler.updateMaxSize(mExpandedBounds.width(), mExpandedBounds.height());
@@ -489,7 +486,7 @@ public class PipTouchHandler {
} else {
final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu();
final Rect toMovementBounds = new Rect();
- mSnapAlgorithm.getMovementBounds(curBounds, insetBounds,
+ mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds,
toMovementBounds, mIsImeShowing ? mImeHeight : 0);
final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
// This is to handle landscape fullscreen IMEs, don't apply the extra offset in this
@@ -500,8 +497,8 @@ public class PipTouchHandler {
if (isExpanded) {
curBounds.set(mExpandedBounds);
- mSnapAlgorithm.applySnapFraction(curBounds, toMovementBounds,
- mSavedSnapFraction);
+ mPipBoundsHandler.getSnapAlgorithm().applySnapFraction(curBounds,
+ toMovementBounds, mSavedSnapFraction);
}
if (prevBottom < toBottom) {
@@ -608,7 +605,7 @@ public class PipTouchHandler {
.spring(DynamicAnimation.TRANSLATION_Y,
mTargetViewContainer.getHeight(),
mTargetSpringConfig)
- .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE))
+ .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE))
.start();
((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition(
@@ -844,8 +841,8 @@ public class PipTouchHandler {
if (mDeferResizeToNormalBoundsUntilRotation == -1) {
Rect restoreBounds = new Rect(getUserResizeBounds());
Rect restoredMovementBounds = new Rect();
- mSnapAlgorithm.getMovementBounds(restoreBounds, mInsetBounds,
- restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
+ mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(restoreBounds,
+ mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
restoredMovementBounds, mMovementBounds, false /* immediate */);
mSavedSnapFraction = -1f;
@@ -1025,25 +1022,25 @@ public class PipTouchHandler {
mMenuController.hideMenu();
}
}
- };
+ }
/**
* Updates the current movement bounds based on whether the menu is currently visible and
* resized.
*/
private void updateMovementBounds() {
- mSnapAlgorithm.getMovementBounds(mMotionHelper.getBounds(), mInsetBounds,
- mMovementBounds, mIsImeShowing ? mImeHeight : 0);
+ mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mMotionHelper.getBounds(),
+ mInsetBounds, mMovementBounds, mIsImeShowing ? mImeHeight : 0);
mMotionHelper.setCurrentMovementBounds(mMovementBounds);
boolean isMenuExpanded = mMenuState == MENU_STATE_FULL;
mPipBoundsHandler.setMinEdgeSize(
- isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize : 0);
+ isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize : 0);
}
private Rect getMovementBounds(Rect curBounds) {
Rect movementBounds = new Rect();
- mSnapAlgorithm.getMovementBounds(curBounds, mInsetBounds,
+ mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, mInsetBounds,
movementBounds, mIsImeShowing ? mImeHeight : 0);
return movementBounds;
}
@@ -1075,6 +1072,7 @@ public class PipTouchHandler {
pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge);
pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets);
+ mPipBoundsHandler.dump(pw, innerPrefix);
mTouchState.dump(pw, innerPrefix);
mMotionHelper.dump(pw, innerPrefix);
if (mPipResizeGestureHandler != null) {
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 74dc003bd8b6..0531ca195f95 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import android.annotation.NonNull;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityTaskManager;
@@ -54,11 +55,15 @@ import com.android.systemui.pip.BasePipManager;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
import java.util.ArrayList;
import java.util.List;
@@ -111,6 +116,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
private Context mContext;
private PipBoundsHandler mPipBoundsHandler;
private PipTaskOrganizer mPipTaskOrganizer;
+ private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
private IActivityTaskManager mActivityTaskManager;
private MediaSessionManager mMediaSessionManager;
private int mState = STATE_NO_PIP;
@@ -229,17 +235,18 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
@Inject
public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
- PipBoundsHandler pipBoundsHandler,
- PipTaskOrganizer pipTaskOrganizer,
- PipSurfaceTransactionHelper surfaceTransactionHelper,
- Divider divider) {
+ ConfigurationController configController,
+ DisplayController displayController,
+ Divider divider,
+ @NonNull PipUiEventLogger pipUiEventLogger,
+ ShellTaskOrganizer shellTaskOrganizer) {
if (mInitialized) {
return;
}
mInitialized = true;
mContext = context;
- mPipBoundsHandler = pipBoundsHandler;
+ mPipBoundsHandler = new PipBoundsHandler(mContext);
// Ensure that we have the display info in case we get calls to update the bounds before the
// listener calls back
final DisplayInfo displayInfo = new DisplayInfo();
@@ -248,7 +255,10 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
mResizeAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
- mPipTaskOrganizer = pipTaskOrganizer;
+ mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context, configController);
+ mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler,
+ mPipSurfaceTransactionHelper, divider, displayController, pipUiEventLogger,
+ shellTaskOrganizer);
mPipTaskOrganizer.registerPipTransitionCallback(this);
mActivityTaskManager = ActivityTaskManager.getService();
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -267,7 +277,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
try {
WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
- mPipTaskOrganizer.registerOrganizer(WINDOWING_MODE_PINNED);
} catch (RemoteException | UnsupportedOperationException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index 20fa991dcc1f..6b42f2e07bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -22,6 +22,7 @@ import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_VENDOR_GESTURE;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -37,6 +38,8 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum {
SCREENSHOT_REQUESTED_OVERVIEW(304),
@UiEvent(doc = "screenshot requested from accessibility actions")
SCREENSHOT_REQUESTED_ACCESSIBILITY_ACTIONS(382),
+ @UiEvent(doc = "screenshot requested from vendor gesture")
+ SCREENSHOT_REQUESTED_VENDOR_GESTURE(638),
@UiEvent(doc = "screenshot requested (other)")
SCREENSHOT_REQUESTED_OTHER(305),
@UiEvent(doc = "screenshot was saved")
@@ -81,6 +84,8 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum {
return ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW;
case SCREENSHOT_ACCESSIBILITY_ACTIONS:
return ScreenshotEvent.SCREENSHOT_REQUESTED_ACCESSIBILITY_ACTIONS;
+ case SCREENSHOT_VENDOR_GESTURE:
+ return ScreenshotEvent.SCREENSHOT_REQUESTED_VENDOR_GESTURE;
case SCREENSHOT_OTHER:
default:
return ScreenshotEvent.SCREENSHOT_REQUESTED_OTHER;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
new file mode 100644
index 000000000000..91ef3c360186
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot.dagger;
+
+import android.app.Service;
+
+import com.android.systemui.screenshot.TakeScreenshotService;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * Defines injectable resources for Screenshots
+ */
+@Module
+public abstract class ScreenshotModule {
+
+ /** */
+ @Binds
+ @IntoMap
+ @ClassKey(TakeScreenshotService.class)
+ public abstract Service bindTakeScreenshotService(TakeScreenshotService service);
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
index 1ee8abb411b9..9b5833b4c116 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
@@ -35,6 +35,7 @@ import android.window.WindowOrganizer;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.systemui.R;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
@@ -91,7 +92,8 @@ public class DividerController implements DisplayController.OnDisplaysChangedLis
public DividerController(Context context,
DisplayController displayController, SystemWindows systemWindows,
- DisplayImeController imeController, Handler handler, TransactionPool transactionPool) {
+ DisplayImeController imeController, Handler handler, TransactionPool transactionPool,
+ ShellTaskOrganizer shellTaskOrganizer) {
mContext = context;
mDisplayController = displayController;
mSystemWindows = systemWindows;
@@ -100,7 +102,7 @@ public class DividerController implements DisplayController.OnDisplaysChangedLis
mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
mTransactionPool = transactionPool;
mWindowManagerProxy = new WindowManagerProxy(mTransactionPool, mHandler);
- mSplits = new SplitScreenTaskOrganizer(this);
+ mSplits = new SplitScreenTaskOrganizer(this, shellTaskOrganizer);
mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler);
mRotationController =
(display, fromRotation, toRotation, wct) -> {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
index 9c2139772e7d..9acb96bb45e9 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
@@ -22,6 +22,7 @@ import android.os.Handler;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.SystemWindows;
@@ -39,10 +40,11 @@ public class DividerModule {
@Provides
static Divider provideDivider(Context context, DisplayController displayController,
SystemWindows systemWindows, DisplayImeController imeController, @Main Handler handler,
- KeyguardStateController keyguardStateController, TransactionPool transactionPool) {
+ KeyguardStateController keyguardStateController, TransactionPool transactionPool,
+ ShellTaskOrganizer shellTaskOrganizer) {
// TODO(b/161116823): fetch DividerProxy from WM shell lib.
DividerController dividerController = new DividerController(context, displayController,
- systemWindows, imeController, handler, transactionPool);
+ systemWindows, imeController, handler, transactionPool, shellTaskOrganizer);
return new Divider(context, dividerController, keyguardStateController);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index ef5e8a15882c..ffd0c7c061d6 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -33,10 +33,14 @@ import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.TaskOrganizer;
-class SplitScreenTaskOrganizer extends TaskOrganizer {
+import com.android.wm.shell.ShellTaskOrganizer;
+
+class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "SplitScreenTaskOrg";
private static final boolean DEBUG = DividerController.DEBUG;
+ private final ShellTaskOrganizer mTaskOrganizer;
+
RunningTaskInfo mPrimary;
RunningTaskInfo mSecondary;
SurfaceControl mPrimarySurface;
@@ -49,13 +53,15 @@ class SplitScreenTaskOrganizer extends TaskOrganizer {
final SurfaceSession mSurfaceSession = new SurfaceSession();
- SplitScreenTaskOrganizer(DividerController dividerController) {
+ SplitScreenTaskOrganizer(DividerController dividerController,
+ ShellTaskOrganizer shellTaskOrganizer) {
mDividerController = dividerController;
+ mTaskOrganizer = shellTaskOrganizer;
+ mTaskOrganizer.addListener(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
}
void init() throws RemoteException {
- registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
synchronized (this) {
try {
mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
@@ -64,7 +70,7 @@ class SplitScreenTaskOrganizer extends TaskOrganizer {
WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
} catch (Exception e) {
// teardown to prevent callbacks
- unregisterOrganizer();
+ mTaskOrganizer.removeListener(this);
throw e;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 1fd0b03a77b7..24515f7bc210 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -164,6 +164,13 @@ public interface NotificationShadeWindowController extends RemoteInputController
default void setRequestTopUi(boolean requestTopUi, String componentTag) {}
/**
+ * Under low light conditions, we might want to increase the display brightness on devices that
+ * don't have an IR camera.
+ * @param brightness float from 0 to 1 or {@code LayoutParams.BRIGHTNESS_OVERRIDE_NONE}
+ */
+ default void setFaceAuthDisplayBrightness(float brightness) {}
+
+ /**
* Custom listener to pipe data back to plugins about whether or not the status bar would be
* collapsed if not for the plugin.
* TODO: Find cleaner way to do this.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
index 17e62890aadd..3bfdf5caa9b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
@@ -16,9 +16,9 @@
package com.android.systemui.statusbar.notification.collection
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection
/**
* Stores the state that [ShadeListBuilder] assigns to this [ListEntry]
@@ -35,7 +35,6 @@ data class ListAttachState private constructor(
* parent's section. Null if not attached to the list.
*/
var section: NotifSection?,
- var sectionIndex: Int,
/**
* If a [NotifFilter] is excluding this entry from the list, then that filter. Always null for
@@ -60,7 +59,6 @@ data class ListAttachState private constructor(
fun clone(other: ListAttachState) {
parent = other.parent
section = other.section
- sectionIndex = other.sectionIndex
excludingFilter = other.excludingFilter
promoter = other.promoter
suppressedChanges.clone(other.suppressedChanges)
@@ -70,7 +68,6 @@ data class ListAttachState private constructor(
fun reset() {
parent = null
section = null
- sectionIndex = -1
excludingFilter = null
promoter = null
suppressedChanges.reset()
@@ -82,7 +79,6 @@ data class ListAttachState private constructor(
return ListAttachState(
null,
null,
- -1,
null,
null,
SuppressedAttachState.create())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index 786c97d03712..52c5c3e08118 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -112,11 +112,9 @@ public class ListDumper {
.append(")");
}
- if (entry.getNotifSection() != null) {
- sb.append(" sectionIndex=")
- .append(entry.getSection())
- .append(" sectionName=")
- .append(entry.getNotifSection().getName());
+ if (entry.getSection() != null) {
+ sb.append(" section=")
+ .append(entry.getSection().getLabel());
}
if (includeRecordKeeping) {
@@ -175,12 +173,9 @@ public class ListDumper {
}
if (notifEntry.getAttachState().getSuppressedChanges().getSection() != null) {
- rksb.append("suppressedSectionIndex=")
+ rksb.append("suppressedSection=")
.append(notifEntry.getAttachState().getSuppressedChanges()
- .getSectionIndex())
- .append(" sectionName=")
- .append(notifEntry.getAttachState().getSuppressedChanges()
- .getSection().getName())
+ .getSection())
.append(" ");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index 65f5dc4e5f7c..82c1f243dcdb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -21,7 +21,7 @@ import android.annotation.UptimeMillisLong;
import androidx.annotation.Nullable;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
/**
* Abstract superclass for top-level entries, i.e. things that can appear in the final notification
@@ -78,13 +78,12 @@ public abstract class ListEntry {
return mPreviousAttachState.getParent();
}
- /** The section this notification was assigned to (0 to N-1, where N is number of sections). */
- public int getSection() {
- return mAttachState.getSectionIndex();
+ @Nullable public NotifSection getSection() {
+ return mAttachState.getSection();
}
- @Nullable public NotifSection getNotifSection() {
- return mAttachState.getSection();
+ public int getSectionIndex() {
+ return mAttachState.getSection() != null ? mAttachState.getSection().getIndex() : -1;
}
ListAttachState getAttachState() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
index 05dd4df1f2ce..a1844ff5d221 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
@@ -24,7 +24,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -155,10 +155,10 @@ public class NotifPipeline implements CommonNotifCollection {
* Sections that are used to sort top-level entries. If two entries have the same section,
* NotifComparators are consulted. Sections from this list are called in order for each
* notification passed through the pipeline. The first NotifSection to return true for
- * {@link NotifSection#isInSection(ListEntry)} sets the entry as part of its Section.
+ * {@link NotifSectioner#isInSection(ListEntry)} sets the entry as part of its Section.
*/
- public void setSections(List<NotifSection> sections) {
- mShadeListBuilder.setSections(sections);
+ public void setSections(List<NotifSectioner> sections) {
+ mShadeListBuilder.setSectioners(sections);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 6cbebf803511..2b545c56c8bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -28,10 +28,11 @@ import static com.android.systemui.statusbar.notification.collection.listbuilder
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.util.ArrayMap;
-import android.util.Pair;
import androidx.annotation.NonNull;
@@ -39,6 +40,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.NotificationInteractionTracker;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
@@ -48,7 +50,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeL
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
@@ -119,6 +121,8 @@ public class ShadeListBuilder implements Dumpable {
mLogger = logger;
mInteractionTracker = interactionTracker;
dumpManager.registerDumpable(TAG, this);
+
+ setSectioners(Collections.emptyList());
}
/**
@@ -193,15 +197,17 @@ public class ShadeListBuilder implements Dumpable {
promoter.setInvalidationListener(this::onPromoterInvalidated);
}
- void setSections(List<NotifSection> sections) {
+ void setSectioners(List<NotifSectioner> sectioners) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
mNotifSections.clear();
- for (NotifSection section : sections) {
- mNotifSections.add(section);
- section.setInvalidationListener(this::onNotifSectionInvalidated);
+ for (NotifSectioner sectioner : sectioners) {
+ mNotifSections.add(new NotifSection(sectioner, mNotifSections.size()));
+ sectioner.setInvalidationListener(this::onNotifSectionInvalidated);
}
+
+ mNotifSections.add(new NotifSection(DEFAULT_SECTIONER, mNotifSections.size()));
}
void setNotifStabilityManager(NotifStabilityManager notifStabilityManager) {
@@ -275,7 +281,7 @@ public class ShadeListBuilder implements Dumpable {
rebuildListIfBefore(STATE_TRANSFORMING);
}
- private void onNotifSectionInvalidated(NotifSection section) {
+ private void onNotifSectionInvalidated(NotifSectioner section) {
Assert.isMainThread();
mLogger.logNotifSectionInvalidated(section.getName(), mPipelineState.getState());
@@ -652,7 +658,6 @@ public class ShadeListBuilder implements Dumpable {
*/
private void annulAddition(ListEntry entry) {
entry.setParent(null);
- entry.getAttachState().setSectionIndex(-1);
entry.getAttachState().setSection(null);
entry.getAttachState().setPromoter(null);
if (entry.mFirstAddedIteration == mIterationCount) {
@@ -663,12 +668,12 @@ public class ShadeListBuilder implements Dumpable {
private void sortList() {
// Assign sections to top-level elements and sort their children
for (ListEntry entry : mNotifList) {
- Pair<NotifSection, Integer> sectionWithIndex = applySections(entry);
+ NotifSection section = applySections(entry);
if (entry instanceof GroupEntry) {
GroupEntry parent = (GroupEntry) entry;
for (NotificationEntry child : parent.getChildren()) {
- child.getAttachState().setSection(sectionWithIndex.first);
- child.getAttachState().setSectionIndex(sectionWithIndex.second);
+ child.getAttachState().setSection(section);
+ child.getAttachState().setSection(section);
}
parent.sortChildren(sChildComparator);
}
@@ -736,16 +741,13 @@ public class ShadeListBuilder implements Dumpable {
mLogger.logSectionChanged(
mIterationCount,
prev.getSection(),
- prev.getSectionIndex(),
- curr.getSection(),
- curr.getSectionIndex());
+ curr.getSection());
}
if (curr.getSuppressedChanges().getSection() != null) {
mLogger.logSectionChangeSuppressed(
mIterationCount,
curr.getSuppressedChanges().getSection(),
- curr.getSuppressedChanges().getSectionIndex(),
curr.getSection());
}
}
@@ -762,7 +764,10 @@ public class ShadeListBuilder implements Dumpable {
callOnCleanup(mNotifPromoters);
callOnCleanup(mNotifFinalizeFilters);
callOnCleanup(mNotifComparators);
- callOnCleanup(mNotifSections);
+
+ for (int i = 0; i < mNotifSections.size(); i++) {
+ mNotifSections.get(i).getSectioner().onCleanup();
+ }
if (mNotifStabilityManager != null) {
callOnCleanup(List.of(mNotifStabilityManager));
@@ -777,7 +782,9 @@ public class ShadeListBuilder implements Dumpable {
private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> {
- int cmp = Integer.compare(o1.getSection(), o2.getSection());
+ int cmp = Integer.compare(
+ requireNonNull(o1.getSection()).getIndex(),
+ requireNonNull(o2.getSection()).getIndex());
if (cmp == 0) {
for (int i = 0; i < mNotifComparators.size(); i++) {
@@ -855,45 +862,41 @@ public class ShadeListBuilder implements Dumpable {
return null;
}
- private Pair<NotifSection, Integer> applySections(ListEntry entry) {
- Pair<NotifSection, Integer> sectionWithIndex = findSection(entry);
+ private NotifSection applySections(ListEntry entry) {
+ final NotifSection newSection = findSection(entry);
final ListAttachState prevAttachState = entry.getPreviousAttachState();
+ NotifSection finalSection = newSection;
+
// are we changing sections of this entry?
if (mNotifStabilityManager != null
&& prevAttachState.getParent() != null
- && (sectionWithIndex.first != prevAttachState.getSection()
- || sectionWithIndex.second != prevAttachState.getSectionIndex())) {
+ && newSection != prevAttachState.getSection()) {
// are section changes allowed?
- if (!mNotifStabilityManager.isSectionChangeAllowed(
- entry.getRepresentativeEntry())) {
- entry.getAttachState().getSuppressedChanges().setSection(
- sectionWithIndex.first);
- entry.getAttachState().getSuppressedChanges().setSectionIndex(
- sectionWithIndex.second);
+ if (!mNotifStabilityManager.isSectionChangeAllowed(entry.getRepresentativeEntry())) {
+ // record the section that we wanted to change to
+ entry.getAttachState().getSuppressedChanges().setSection(newSection);
// keep the previous section
- sectionWithIndex = new Pair(
- prevAttachState.getSection(),
- prevAttachState.getSectionIndex());
+ finalSection = prevAttachState.getSection();
}
}
- entry.getAttachState().setSection(sectionWithIndex.first);
- entry.getAttachState().setSectionIndex(sectionWithIndex.second);
+ entry.getAttachState().setSection(finalSection);
- return sectionWithIndex;
+ return finalSection;
}
- private Pair<NotifSection, Integer> findSection(ListEntry entry) {
+ @NonNull
+ private NotifSection findSection(ListEntry entry) {
for (int i = 0; i < mNotifSections.size(); i++) {
- NotifSection sectioner = mNotifSections.get(i);
- if (sectioner.isInSection(entry)) {
- return new Pair<>(sectioner, i);
+ NotifSection section = mNotifSections.get(i);
+ if (section.getSectioner().isInSection(entry)) {
+ return section;
}
}
- return new Pair<>(sDefaultSection, mNotifSections.size());
+ throw new RuntimeException("Missing default sectioner!");
}
private void rebuildListIfBefore(@PipelineState.StateName int state) {
@@ -963,15 +966,15 @@ public class ShadeListBuilder implements Dumpable {
void onRenderList(@NonNull List<ListEntry> entries);
}
- private static final NotifSection sDefaultSection =
- new NotifSection("UnknownSection") {
+ private static final NotifSectioner DEFAULT_SECTIONER =
+ new NotifSectioner("UnknownSection") {
@Override
public boolean isInSection(ListEntry entry) {
return true;
}
};
- private static final String TAG = "ShadeListBuilder";
-
private static final int MIN_CHILDREN_FOR_GROUP = 2;
+
+ private static final String TAG = "ShadeListBuilder";
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
index 52612365712e..3eb2e610f329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
/**
* Stores the suppressed state that [ShadeListBuilder] assigned to this [ListEntry] before the
@@ -33,22 +33,19 @@ data class SuppressedAttachState private constructor(
* The assigned section for this ListEntry. If the child of the group, this will be the
* parent's section. Null if not attached to the list.
*/
- var section: NotifSection?,
- var sectionIndex: Int
+ var section: NotifSection?
) {
/** Copies the state of another instance. */
fun clone(other: SuppressedAttachState) {
parent = other.parent
section = other.section
- sectionIndex = other.sectionIndex
}
/** Resets back to a "clean" state (the same as created by the factory method) */
fun reset() {
parent = null
section = null
- sectionIndex = -1
}
companion object {
@@ -56,8 +53,7 @@ data class SuppressedAttachState private constructor(
fun create(): SuppressedAttachState {
return SuppressedAttachState(
null,
- null,
- -1)
+ null)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
index 0b9bded5ef58..c7ac40346ce1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
@@ -29,7 +29,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -83,8 +83,8 @@ public class AppOpsCoordinator implements Coordinator {
}
- public NotifSection getSection() {
- return mNotifSection;
+ public NotifSectioner getSectioner() {
+ return mNotifSectioner;
}
/**
@@ -179,7 +179,7 @@ public class AppOpsCoordinator implements Coordinator {
/**
* Puts foreground service notifications into its own section.
*/
- private final NotifSection mNotifSection = new NotifSection("ForegroundService") {
+ private final NotifSectioner mNotifSectioner = new NotifSectioner("ForegroundService") {
@Override
public boolean isInSection(ListEntry entry) {
NotificationEntry notificationEntry = entry.getRepresentativeEntry();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index c8e859f27a5c..dea11626a3f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -21,7 +21,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
import javax.inject.Inject
@@ -42,7 +42,7 @@ class ConversationCoordinator @Inject constructor(
}
}
- private val mNotifSection: NotifSection = object : NotifSection("People") {
+ val sectioner = object : NotifSectioner("People") {
override fun isInSection(entry: ListEntry): Boolean {
return isConversation(entry.representativeEntry!!)
}
@@ -52,10 +52,6 @@ class ConversationCoordinator @Inject constructor(
pipeline.addPromoter(notificationPromoter)
}
- fun getSection(): NotifSection {
- return mNotifSection
- }
-
private fun isConversation(entry: NotificationEntry): Boolean =
peopleNotificationIdentifier.getPeopleNotificationType(entry.sbn, entry.ranking) !=
TYPE_NON_PERSON
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
index 6e6cecaf62fa..c023400ca9ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -27,7 +27,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
@@ -88,8 +88,8 @@ public class HeadsUpCoordinator implements Coordinator {
pipeline.addNotificationLifetimeExtender(mLifetimeExtender);
}
- public NotifSection getSection() {
- return mNotifSection;
+ public NotifSectioner getSectioner() {
+ return mNotifSectioner;
}
private void onHeadsUpViewBound(NotificationEntry entry) {
@@ -191,7 +191,7 @@ public class HeadsUpCoordinator implements Coordinator {
}
};
- private final NotifSection mNotifSection = new NotifSection("HeadsUp") {
+ private final NotifSectioner mNotifSectioner = new NotifSectioner("HeadsUp") {
@Override
public boolean isInSection(ListEntry entry) {
return isCurrentlyShowingHun(entry);
@@ -207,7 +207,7 @@ public class HeadsUpCoordinator implements Coordinator {
endNotifLifetimeExtension();
mCurrentHun = newHUN;
mNotifPromoter.invalidateList();
- mNotifSection.invalidateList();
+ mNotifSectioner.invalidateList();
}
if (!isHeadsUp) {
mHeadsUpViewBinder.unbindHeadsUpView(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 87ca717982f5..ded5e46593f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -21,7 +21,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
@@ -41,7 +41,7 @@ import javax.inject.Inject;
public class NotifCoordinators implements Dumpable {
private static final String TAG = "NotifCoordinators";
private final List<Coordinator> mCoordinators = new ArrayList<>();
- private final List<NotifSection> mOrderedSections = new ArrayList<>();
+ private final List<NotifSectioner> mOrderedSections = new ArrayList<>();
/**
* Creates all the coordinators.
@@ -81,12 +81,12 @@ public class NotifCoordinators implements Dumpable {
// Manually add Ordered Sections
// HeadsUp > FGS > People > Alerting > Silent > Unknown/Default
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
- mOrderedSections.add(headsUpCoordinator.getSection()); // HeadsUp
+ mOrderedSections.add(headsUpCoordinator.getSectioner()); // HeadsUp
}
- mOrderedSections.add(appOpsCoordinator.getSection()); // ForegroundService
- mOrderedSections.add(conversationCoordinator.getSection()); // People
- mOrderedSections.add(rankingCoordinator.getAlertingSection()); // Alerting
- mOrderedSections.add(rankingCoordinator.getSilentSection()); // Silent
+ mOrderedSections.add(appOpsCoordinator.getSectioner()); // ForegroundService
+ mOrderedSections.add(conversationCoordinator.getSectioner()); // People
+ mOrderedSections.add(rankingCoordinator.getAlertingSectioner()); // Alerting
+ mOrderedSections.add(rankingCoordinator.getSilentSectioner()); // Silent
}
/**
@@ -109,7 +109,7 @@ public class NotifCoordinators implements Dumpable {
pw.println("\t" + c.getClass());
}
- for (NotifSection s : mOrderedSections) {
+ for (NotifSectioner s : mOrderedSections) {
pw.println("\t" + s.getName());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index a32b1636057b..0f08e0ff491c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -22,7 +22,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import javax.inject.Inject;
@@ -57,22 +57,22 @@ public class RankingCoordinator implements Coordinator {
pipeline.addPreGroupFilter(mDozingFilter);
}
- public NotifSection getAlertingSection() {
- return mAlertingNotifSection;
+ public NotifSectioner getAlertingSectioner() {
+ return mAlertingNotifSectioner;
}
- public NotifSection getSilentSection() {
- return mSilentNotifSection;
+ public NotifSectioner getSilentSectioner() {
+ return mSilentNotifSectioner;
}
- private final NotifSection mAlertingNotifSection = new NotifSection("Alerting") {
+ private final NotifSectioner mAlertingNotifSectioner = new NotifSectioner("Alerting") {
@Override
public boolean isInSection(ListEntry entry) {
return mHighPriorityProvider.isHighPriority(entry);
}
};
- private final NotifSection mSilentNotifSection = new NotifSection("Silent") {
+ private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent") {
@Override
public boolean isInSection(ListEntry entry) {
return !mHighPriorityProvider.isHighPriority(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/dagger/OneHandedModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
index fe5fa2b5fccd..c09122ea3c26 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/dagger/OneHandedModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
@@ -14,23 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.onehanded.dagger;
+package com.android.systemui.statusbar.notification.collection.listbuilder
-import com.android.systemui.onehanded.OneHandedManager;
-import com.android.systemui.onehanded.OneHandedManagerImpl;
-
-import dagger.Binds;
-import dagger.Module;
-
-/**
- * Dagger Module for One handed.
- */
-@Module
-public abstract class OneHandedModule {
-
- /** Binds OneHandedManager as the default. */
- @Binds
- public abstract OneHandedManager provideOneHandedManager(
- OneHandedManagerImpl oneHandedManagerImpl);
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+data class NotifSection(
+ val sectioner: NotifSectioner,
+ val index: Int
+) {
+ val label: String
+ get() = "Section($index, \"${sectioner.name}\")"
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index f7bfeb7234f0..9ee7db738c20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -25,7 +25,6 @@ import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection
import javax.inject.Inject
class ShadeListBuilderLogger @Inject constructor(
@@ -211,21 +210,17 @@ class ShadeListBuilderLogger @Inject constructor(
fun logSectionChanged(
buildId: Int,
prevSection: NotifSection?,
- prevIndex: Int,
- newSection: NotifSection?,
- newIndex: Int
+ newSection: NotifSection?
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
- str1 = prevSection?.name
- int1 = prevIndex
- str2 = newSection?.name
- int2 = newIndex
+ str1 = prevSection?.label
+ str2 = newSection?.label
}, {
if (str1 == null) {
- "(Build $long1) Section assigned: '$str2' (#$int2)"
+ "(Build $long1) Section assigned: $str2"
} else {
- "(Build $long1) Section changed: '$str1' (#$int1) -> '$str2' (#$int2)"
+ "(Build $long1) Section changed: $str1 -> $str2"
}
})
}
@@ -233,17 +228,14 @@ class ShadeListBuilderLogger @Inject constructor(
fun logSectionChangeSuppressed(
buildId: Int,
suppressedSection: NotifSection?,
- suppressedSectionIndex: Int,
assignedSection: NotifSection?
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
- str1 = suppressedSection?.name
- int1 = suppressedSectionIndex
- str2 = assignedSection?.name
+ str1 = suppressedSection?.label
+ str2 = assignedSection?.label
}, {
- "(Build $long1) Section change suppressed: '$str1' (#$int1). " +
- "Keeping section: '$str2'"
+ "(Build $long1) Suppressing section change to $str1 (staying at $str2)"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
index fe5ba3c8e6fc..b57f504189f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
@@ -22,8 +22,8 @@ import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
/**
* Pluggable for participating in notif sectioning. See {@link ShadeListBuilder#setSections}.
*/
-public abstract class NotifSection extends Pluggable<NotifSection> {
- protected NotifSection(String name) {
+public abstract class NotifSectioner extends Pluggable<NotifSectioner> {
+ protected NotifSectioner(String name) {
super(name);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index d4c270f45ceb..e061472b3939 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -95,21 +95,15 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -145,7 +139,6 @@ import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -154,11 +147,8 @@ import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
-import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.Assert;
@@ -178,7 +168,7 @@ import javax.inject.Named;
/**
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
*/
-public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter, Dumpable {
+public class NotificationStackScrollLayout extends ViewGroup implements Dumpable {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
@@ -195,10 +185,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
* gap is drawn between them). In this case we don't want to round their corners.
*/
private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
- private final KeyguardBypassController mKeyguardBypassController;
+ private OnMenuEventListener mMenuEventListener;
+ private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
private final DynamicPrivacyController mDynamicPrivacyController;
private final SysuiStatusBarStateController mStatusbarStateController;
- private final KeyguardMediaController mKeyguardMediaController;
private ExpandHelper mExpandHelper;
private final NotificationSwipeHelper mSwipeHelper;
@@ -249,6 +239,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private int mBottomMargin;
private int mBottomInset = 0;
private float mQsExpansionFraction;
+ private int mCurrentUserId;
/**
* The algorithm which calculates the properties for our children
@@ -326,7 +317,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
* motion.
*/
private int mMaxScrollAfterExpand;
- private ExpandableNotificationRow.LongPressListener mLongPressListener;
boolean mCheckForLeavebehind;
/**
@@ -348,12 +338,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
return true;
}
};
- private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() {
- @Override
- public void onUserChanged(int userId) {
- updateSensitiveness(false /* animated */);
- }
- };
+
private StatusBar mStatusBar;
private int[] mTempInt2 = new int[2];
private boolean mGenerateChildOrderChangedEvent;
@@ -368,7 +353,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private boolean mForceNoOverlappingRendering;
private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
private FalsingManager mFalsingManager;
- private final ZenModeController mZenController;
private boolean mAnimationRunning;
private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
= new ViewTreeObserver.OnPreDrawListener() {
@@ -498,7 +482,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
- private final NotificationLockscreenUserManager mLockscreenUserManager;
private final Rect mTmpRect = new Rect();
private final FeatureFlags mFeatureFlags;
private final NotifPipeline mNotifPipeline;
@@ -511,7 +494,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
protected final UiEventLogger mUiEventLogger;
private final NotificationRemoteInputManager mRemoteInputManager =
Dependency.get(NotificationRemoteInputManager.class);
- private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
private final LockscreenGestureLogger mLockscreenGestureLogger =
@@ -536,11 +518,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private int mWaterfallTopInset;
private NotificationStackScrollLayoutController mController;
- private SysuiColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
- (colorExtractor, which) -> {
- final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText();
- updateDecorViews(useDarkText);
- };
+ private boolean mKeyguardMediaControllorVisible;
private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener =
new ExpandableView.OnHeightChangedListener() {
@@ -555,20 +533,44 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
};
+ private final ScrollAdapter mScrollAdapter = new ScrollAdapter() {
+ @Override
+ public boolean isScrolledToTop() {
+ if (ANCHOR_SCROLLING) {
+ updateScrollAnchor();
+ // TODO: once we're recycling this will need to check the adapter position of the
+ // child
+ return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0;
+ } else {
+ return mOwnScrollY == 0;
+ }
+ }
+
+ @Override
+ public boolean isScrolledToBottom() {
+ if (ANCHOR_SCROLLING) {
+ return getMaxPositiveScrollAmount() <= 0;
+ } else {
+ return mOwnScrollY >= getScrollRange();
+ }
+ }
+
+ @Override
+ public View getHostView() {
+ return NotificationStackScrollLayout.this;
+ }
+ };
+
@Inject
public NotificationStackScrollLayout(
@Named(VIEW_CONTEXT) Context context,
AttributeSet attrs,
NotificationRoundnessManager notificationRoundnessManager,
DynamicPrivacyController dynamicPrivacyController,
- SysuiStatusBarStateController statusBarStateController,
+ SysuiStatusBarStateController statusbarStateController,
HeadsUpManagerPhone headsUpManager,
- KeyguardBypassController keyguardBypassController,
- KeyguardMediaController keyguardMediaController,
FalsingManager falsingManager,
- NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGutsManager notificationGutsManager,
- ZenModeController zenController,
NotificationSectionsManager notificationSectionsManager,
ForegroundServiceSectionController fgsSectionController,
ForegroundServiceDismissalFeatureController fgsFeatureController,
@@ -583,13 +585,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mRoundnessManager = notificationRoundnessManager;
- mLockscreenUserManager = notificationLockscreenUserManager;
mNotificationGutsManager = notificationGutsManager;
mHeadsUpManager = headsUpManager;
mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
- mKeyguardBypassController = keyguardBypassController;
mFalsingManager = falsingManager;
- mZenController = zenController;
mFgsSectionController = fgsSectionController;
mSectionsManager = notificationSectionsManager;
@@ -608,16 +607,33 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
minHeight, maxHeight);
mExpandHelper.setEventSource(this);
- mExpandHelper.setScrollAdapter(this);
+ mExpandHelper.setScrollAdapter(mScrollAdapter);
+
+ // TODO: move swipe helper into controller.
+ // The anonymous proxy through to mMenuEventListener is temporary until more can be moved
+ // into the controller.
mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback,
- getContext(), mMenuEventListener, mFalsingManager);
+ getContext(), new OnMenuEventListener() {
+ @Override
+ public void onMenuClicked(View row, int x, int y, MenuItem menu) {
+ mMenuEventListener.onMenuClicked(row, x, y, menu);
+ }
+
+ @Override
+ public void onMenuReset(View row) {
+ mMenuEventListener.onMenuReset(row);
+ }
+
+ @Override
+ public void onMenuShown(View row) {
+ mMenuEventListener.onMenuShown(row);
+ }
+ }, mFalsingManager);
mStackScrollAlgorithm = createStackScrollAlgorithm(context);
- initView(context);
mShouldDrawNotificationBackground =
res.getBoolean(R.bool.config_drawNotificationBackground);
mFadeNotificationsOnDismiss =
res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
- mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
setOutlineProvider(mOutlineProvider);
// Blocking helper manager wants to know the expanded state, update as well.
@@ -667,20 +683,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
mDynamicPrivacyController = dynamicPrivacyController;
- mStatusbarStateController = statusBarStateController;
+ mStatusbarStateController = statusbarStateController;
initializeForegroundServiceSection(fgsFeatureController);
mUiEventLogger = uiEventLogger;
- mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
- mKeyguardMediaController = keyguardMediaController;
- keyguardMediaController.setVisibilityChangedListener((visible) -> {
- if (visible) {
- generateAddAnimation(keyguardMediaController.getView(), false /*fromMoreCard */);
- } else {
- generateRemoveAnimation(keyguardMediaController.getView());
- }
- requestChildrenUpdate();
- return null;
- });
}
private void initializeForegroundServiceSection(
@@ -719,7 +724,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
public float getWakeUpHeight() {
ExpandableView firstChild = getFirstChildWithBackground();
if (firstChild != null) {
- if (mKeyguardBypassController.getBypassEnabled()) {
+ if (mKeyguardBypassEnabledProvider.getBypassEnabled()) {
return firstChild.getHeadsUpHeightWithoutHeader();
} else {
return firstChild.getCollapsedHeight();
@@ -794,21 +799,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
};
}
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class))
- .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER);
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
- }
-
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public NotificationSwipeActionHelper getSwipeActionHelper() {
return mSwipeHelper;
@@ -898,7 +888,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
}
boolean shouldDrawBackground;
- if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) {
+ if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) {
shouldDrawBackground = isPulseExpanding();
} else {
shouldDrawBackground = !mAmbientState.isDozing() || anySectionHasVisibleChild;
@@ -1013,9 +1003,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
}
+ private void reinitView() {
+ initView(getContext(), mKeyguardBypassEnabledProvider);
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void initView(Context context) {
+ void initView(Context context,
+ KeyguardBypassEnabledProvider keyguardBypassEnabledProvider) {
mScroller = new OverScroller(getContext());
+ mKeyguardBypassEnabledProvider = keyguardBypassEnabledProvider;
+
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setClipChildren(false);
final ViewConfiguration configuration = ViewConfiguration.get(context);
@@ -1227,7 +1224,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
float end = start + child.getActualHeight();
boolean clip = clipStart > start && clipStart < end
|| clipEnd >= start && clipEnd <= end;
- clip &= !(first && isScrolledToTop());
+ clip &= !(first && mScrollAdapter.isScrolledToTop());
child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
: ExpandableView.NO_ROUNDNESS);
first = false;
@@ -1287,7 +1284,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- private void requestChildrenUpdate() {
+ void requestChildrenUpdate() {
if (!mChildrenUpdateRequested) {
getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
mChildrenUpdateRequested = true;
@@ -1417,7 +1414,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private void notifyAppearChangedListeners() {
float appear;
float expandAmount;
- if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) {
+ if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) {
appear = calculateAppearFractionBypass();
expandAmount = getPulseHeight();
} else {
@@ -1819,7 +1816,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mSwipeHelper.setDensityScale(densityScale);
float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
- initView(getContext());
+ reinitView();
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -2220,7 +2217,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
springBack();
} else {
float overScrollTop = getCurrentOverScrollAmount(true /* top */);
- if (isScrolledToTop() && mScrollAnchorViewY > 0) {
+ if (mScrollAdapter.isScrolledToTop() && mScrollAnchorViewY > 0) {
notifyOverscrollTopListener(mScrollAnchorViewY,
isRubberbanded(true /* onTop */));
} else {
@@ -2268,7 +2265,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void springBack() {
if (ANCHOR_SCROLLING) {
- boolean overScrolledTop = isScrolledToTop() && mScrollAnchorViewY > 0;
+ boolean overScrolledTop = mScrollAdapter.isScrolledToTop() && mScrollAnchorViewY > 0;
int maxPositiveScrollAmount = getMaxPositiveScrollAmount();
boolean overscrolledBottom = maxPositiveScrollAmount < 0;
if (overScrolledTop || overscrolledBottom) {
@@ -2545,8 +2542,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateForwardAndBackwardScrollability() {
- boolean forwardScrollable = mScrollable && !isScrolledToBottom();
- boolean backwardsScrollable = mScrollable && !isScrolledToTop();
+ boolean forwardScrollable = mScrollable && !mScrollAdapter.isScrolledToBottom();
+ boolean backwardsScrollable = mScrollable && !mScrollAdapter.isScrolledToTop();
boolean changed = forwardScrollable != mForwardScrollable
|| backwardsScrollable != mBackwardScrollable;
mForwardScrollable = forwardScrollable;
@@ -2667,7 +2664,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
boolean shiftPulsingWithFirst = mHeadsUpManager.getAllEntries().count() <= 1
&& (mAmbientState.isDozing()
- || (mKeyguardBypassController.getBypassEnabled() && onKeyguard));
+ || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard));
for (NotificationSection section : mSections) {
int minBottomPosition = minTopPosition;
if (section == lastSection) {
@@ -2830,7 +2827,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mLastScrollerY = 0;
// x velocity is set to 1 to avoid overscroller bug
mScroller.fling(0, 0, 1, velocityY, 0, 0, minY, maxY, 0,
- mExpandedInThisMotion && !isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2);
+ mExpandedInThisMotion
+ && !mScrollAdapter.isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2);
}
}
@@ -2937,7 +2935,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
} else {
mTopPaddingOverflow = 0;
}
- setTopPadding(topPadding, animate && !mKeyguardBypassController.getBypassEnabled());
+ setTopPadding(topPadding, animate && !mKeyguardBypassEnabledProvider.getBypassEnabled());
setExpandedHeight(mExpandedHeight);
}
@@ -3095,7 +3093,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
* @return Whether an animation was generated.
*/
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- private boolean generateRemoveAnimation(ExpandableView child) {
+ boolean generateRemoveAnimation(ExpandableView child) {
if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
mAddedHeadsUpChildren.remove(child);
return false;
@@ -3332,7 +3330,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- void onViewAddedInternal(ExpandableView child) {
+ private void onViewAddedInternal(ExpandableView child) {
updateHideSensitiveForChild(child);
child.setOnHeightChangedListener(mOnChildHeightChangedListener);
generateAddAnimation(child, false /* fromMoreCard */);
@@ -3343,7 +3341,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
if (ANCHOR_SCROLLING) {
// TODO: once we're recycling this will need to check the adapter position of the child
- if (child == getFirstChildNotGone() && (isScrolledToTop() || !mIsExpanded)) {
+ if (child == getFirstChildNotGone()
+ && (mScrollAdapter.isScrolledToTop() || !mIsExpanded)) {
// New child was added at the top while we're scrolled to the top;
// make it the new anchor view so that we stay at the top.
mScrollAnchorView = child;
@@ -3361,6 +3360,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
onViewRemovedInternal(row, childrenContainer);
}
+ public void notifyGroupChildAdded(ExpandableView row) {
+ onViewAddedInternal(row);
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setAnimationsEnabled(boolean animationsEnabled) {
mAnimationsEnabled = animationsEnabled;
@@ -3533,7 +3536,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
boolean performDisappearAnimation = !mIsExpanded
// Only animate if we still have pinned heads up, otherwise we just have the
// regular collapse animation of the lock screen
- || (mKeyguardBypassController.getBypassEnabled() && onKeyguard()
+ || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()
&& mHeadsUpManager.hasPinnedHeadsUp());
if (performDisappearAnimation && !isHeadsUp) {
type = row.wasJustClicked()
@@ -3769,11 +3772,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
return y < getHeight() - getEmptyBottomMargin();
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
- mLongPressListener = listener;
- }
-
private float getTouchSlop(MotionEvent event) {
// Adjust the touch slop if another gesture may be being performed.
return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
@@ -4227,7 +4225,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
case MotionEvent.ACTION_DOWN: {
final int y = (int) ev.getY();
- mScrolledToTopOnFirstDown = isScrolledToTop();
+ mScrolledToTopOnFirstDown = mScrollAdapter.isScrolledToTop();
final ExpandableView childAtTouchPos = getChildAtPosition(
ev.getX(), y, false /* requireMinHeight */, false /* ignoreDecors */);
if (childAtTouchPos == null) {
@@ -4411,32 +4409,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
}
- @Override
- @ShadeViewRefactor(RefactorComponent.COORDINATOR)
- public boolean isScrolledToTop() {
- if (ANCHOR_SCROLLING) {
- updateScrollAnchor();
- // TODO: once we're recycling this will need to check the adapter position of the child
- return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0;
- } else {
- return mOwnScrollY == 0;
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.COORDINATOR)
- public boolean isScrolledToBottom() {
- if (ANCHOR_SCROLLING) {
- return getMaxPositiveScrollAmount() <= 0;
- } else {
- return mOwnScrollY >= getScrollRange();
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public View getHostView() {
- return this;
+ boolean isScrolledToBottom() {
+ return mScrollAdapter.isScrolledToBottom();
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
@@ -4734,8 +4708,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void updateSensitiveness(boolean animate) {
- boolean hideSensitive = mLockscreenUserManager.isAnyProfilePublicMode();
+ void updateSensitiveness(boolean animate, boolean hideSensitive) {
if (hideSensitive != mAmbientState.isHideSensitive()) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -4927,7 +4900,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
// Since we are clipping to the outline we need to make sure that the shadows aren't
// clipped when pulsing
float ownTranslationZ = 0;
- if (mKeyguardBypassController.getBypassEnabled() && mAmbientState.isHiddenAtAll()) {
+ if (mKeyguardBypassEnabledProvider.getBypassEnabled() && mAmbientState.isHiddenAtAll()) {
ExpandableView firstChildNotGone = getFirstChildNotGone();
if (firstChildNotGone != null && firstChildNotGone.showingPulsing()) {
ownTranslationZ = firstChildNotGone.getTranslationZ();
@@ -4992,11 +4965,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- void updateEmptyShadeView(boolean visible) {
+ void updateEmptyShadeView(boolean visible, boolean notifVisibleInShade) {
mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
int oldTextRes = mEmptyShadeView.getTextResource();
- int newTextRes = mZenController.areNotificationsHiddenInShade()
+ int newTextRes = notifVisibleInShade
? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
if (oldTextRes != newTextRes) {
mEmptyShadeView.setText(newTextRes);
@@ -5093,7 +5066,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- private void requestAnimateEverything() {
+ void requestAnimateEverything() {
if (mIsExpanded && mAnimationsEnabled) {
mEverythingNeedsAnimation = true;
mNeedsAnimation = true;
@@ -5431,17 +5404,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mAmbientState.setStatusBarState(statusBarState);
}
- private void onStatePostChange() {
+ void onStatePostChange(boolean fromShadeLocked) {
boolean onKeyguard = onKeyguard();
+ mAmbientState.setActivatedChild(null);
+ mAmbientState.setDimmed(onKeyguard);
+
if (mHeadsUpAppearanceController != null) {
mHeadsUpAppearanceController.onStateChanged();
}
- SysuiStatusBarStateController state = (SysuiStatusBarStateController)
- Dependency.get(StatusBarStateController.class);
- updateSensitiveness(state.goingToFullShade() /* animate */);
- setDimmed(onKeyguard, state.fromShadeLocked() /* animate */);
+ setDimmed(onKeyguard, fromShadeLocked);
setExpandingEnabled(!onKeyguard);
ActivatableNotificationView activatedChild = getActivatedChild();
setActivatedChild(null);
@@ -5771,7 +5744,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
*/
public float setPulseHeight(float height) {
mAmbientState.setPulseHeight(height);
- if (mKeyguardBypassController.getBypassEnabled()) {
+ if (mKeyguardBypassEnabledProvider.getBypassEnabled()) {
notifyAppearChangedListeners();
}
requestChildrenUpdate();
@@ -5849,6 +5822,31 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
return mController;
}
+ void setCurrentUserid(int userId) {
+ mCurrentUserId = userId;
+ }
+
+ void onMenuShown(View row) {
+ mSwipeHelper.onMenuShown(row);
+ }
+
+ void onMenuReset(View row) {
+ View translatingParentView = mSwipeHelper.getTranslatingParentView();
+ if (translatingParentView != null && row == translatingParentView) {
+ mSwipeHelper.clearExposedMenuView();
+ mSwipeHelper.clearTranslatingParentView();
+ if (row instanceof ExpandableNotificationRow) {
+ mHeadsUpManager.setMenuShown(
+ ((ExpandableNotificationRow) row).getEntry(), false);
+
+ }
+ }
+ }
+
+ void setMenuEventListener(OnMenuEventListener menuEventListener) {
+ mMenuEventListener = menuEventListener;
+ }
+
/**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@@ -6204,89 +6202,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
}
- @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- private final StateListener mStateListener = new StateListener() {
- @Override
- public void onStatePreChange(int oldState, int newState) {
- if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) {
- requestAnimateEverything();
- }
- }
- @Override
- public void onStateChanged(int newState) {
- setStatusBarState(newState);
- }
-
- @Override
- public void onStatePostChange() {
- NotificationStackScrollLayout.this.onStatePostChange();
- }
- };
-
- @VisibleForTesting
- @ShadeViewRefactor(RefactorComponent.INPUT)
- protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
- @Override
- public void onMenuClicked(View view, int x, int y, MenuItem item) {
- if (mLongPressListener == null) {
- return;
- }
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- mMetricsLogger.write(row.getEntry().getSbn().getLogMaker()
- .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
- .setType(MetricsEvent.TYPE_ACTION)
- );
- }
- mLongPressListener.onLongPress(view, x, y, item);
- }
-
- @Override
- public void onMenuReset(View row) {
- View translatingParentView = mSwipeHelper.getTranslatingParentView();
- if (translatingParentView != null && row == translatingParentView) {
- mSwipeHelper.clearExposedMenuView();
- mSwipeHelper.clearTranslatingParentView();
- if (row instanceof ExpandableNotificationRow) {
- mHeadsUpManager.setMenuShown(
- ((ExpandableNotificationRow) row).getEntry(), false);
-
- }
- }
- }
-
- @Override
- public void onMenuShown(View row) {
- if (row instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
- mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker()
- .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
- .setType(MetricsEvent.TYPE_ACTION));
- mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
- mSwipeHelper.onMenuShown(row);
- mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
- false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
- false /* resetMenu */);
-
- // Check to see if we want to go directly to the notfication guts
- NotificationMenuRowPlugin provider = notificationRow.getProvider();
- if (provider.shouldShowGutsOnSnapOpen()) {
- MenuItem item = provider.menuItemToExposeOnSnap();
- if (item != null) {
- Point origin = provider.getRevealAnimationOrigin();
- mNotificationGutsManager.openGuts(row, origin.x, origin.y, item);
- } else {
- Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no "
- + "menu item in menuItemtoExposeOnSnap. Skipping.");
- }
-
- // Close the menu row since we went directly to the guts
- resetExposedMenuView(false, true);
- }
- }
- }
- };
@ShadeViewRefactor(RefactorComponent.INPUT)
private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
@@ -6517,7 +6433,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
@SelectedRows int selectedRows) {
if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
if (selectedRows == ROWS_ALL) {
- mNotifCollection.dismissAllNotifications(mLockscreenUserManager.getCurrentUserId());
+ mNotifCollection.dismissAllNotifications(mCurrentUserId);
} else {
final List<Pair<NotificationEntry, DismissedByUserStats>>
entriesWithRowsDismissedFromShade = new ArrayList<>();
@@ -6546,7 +6462,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
if (selectedRows == ROWS_ALL) {
try {
- mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId());
+ mBarService.onClearAllNotifications(mCurrentUserId);
} catch (Exception ex) {
}
}
@@ -6568,6 +6484,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
NotificationLogger.getNotificationLocation(entry)));
}
+ public void setKeyguardMediaControllorVisible(boolean keyguardMediaControllorVisible) {
+ mKeyguardMediaControllorVisible = keyguardMediaControllorVisible;
+ }
+
// ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
@ShadeViewRefactor(RefactorComponent.INPUT)
@@ -6576,8 +6496,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
/* Only ever called as a consequence of a lockscreen expansion gesture. */
@Override
public boolean onDraggedDown(View startingChild, int dragLengthY) {
- boolean canDragDown = hasActiveNotifications()
- || mKeyguardMediaController.getView().getVisibility() == VISIBLE;
+ boolean canDragDown = hasActiveNotifications() || mKeyguardMediaControllorVisible;
if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) {
mLockscreenGestureLogger.write(
MetricsEvent.ACTION_LS_SHADE,
@@ -6655,7 +6574,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
@Override
public boolean isDragDownAnywhereEnabled() {
return mStatusbarStateController.getState() == StatusBarState.KEYGUARD
- && !mKeyguardBypassController.getBypassEnabled();
+ && !mKeyguardBypassEnabledProvider.getBypassEnabled();
}
};
@@ -6838,4 +6757,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
return INVALID;
}
}
+
+ interface KeyguardBypassEnabledProvider {
+ boolean getBypassEnabled();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 7c29ee2b5483..ca78b2a1fc68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -18,8 +18,10 @@ package com.android.systemui.statusbar.notification.stack;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import android.graphics.Point;
import android.graphics.PointF;
import android.provider.Settings;
+import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
@@ -27,9 +29,20 @@ import android.view.WindowInsets;
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.media.KeyguardMediaController;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -42,6 +55,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -49,6 +63,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
import java.util.function.BiConsumer;
@@ -56,11 +71,15 @@ import java.util.function.BiConsumer;
import javax.inject.Inject;
import javax.inject.Named;
+import kotlin.Unit;
+
/**
* Controller for {@link NotificationStackScrollLayout}.
*/
@StatusBarComponent.StatusBarScope
public class NotificationStackScrollLayoutController {
+ private static final String TAG = "StackScrollerController";
+
private final boolean mAllowLongPress;
private final NotificationGutsManager mNotificationGutsManager;
private final HeadsUpManagerPhone mHeadsUpManager;
@@ -68,9 +87,18 @@ public class NotificationStackScrollLayoutController {
private final TunerService mTunerService;
private final DynamicPrivacyController mDynamicPrivacyController;
private final ConfigurationController mConfigurationController;
+ private final ZenModeController mZenModeController;
+ private final MetricsLogger mMetricsLogger;
+ private final KeyguardMediaController mKeyguardMediaController;
+ private final SysuiStatusBarStateController mStatusBarStateController;
+ private final KeyguardBypassController mKeyguardBypassController;
+ private final SysuiColorExtractor mColorExtractor;
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+
+ private NotificationStackScrollLayout mView;
+
private final NotificationListContainerImpl mNotificationListContainer =
new NotificationListContainerImpl();
- private NotificationStackScrollLayout mView;
@VisibleForTesting
final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
@@ -78,11 +106,14 @@ public class NotificationStackScrollLayoutController {
@Override
public void onViewAttachedToWindow(View v) {
mConfigurationController.addCallback(mConfigurationListener);
+ mStatusBarStateController.addCallback(
+ mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER);
}
@Override
public void onViewDetachedFromWindow(View v) {
mConfigurationController.removeCallback(mConfigurationListener);
+ mStatusBarStateController.removeCallback(mStateListener);
}
};
@@ -122,6 +153,91 @@ public class NotificationStackScrollLayoutController {
}
};
+ private final StatusBarStateController.StateListener mStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStatePreChange(int oldState, int newState) {
+ if (oldState == StatusBarState.SHADE_LOCKED
+ && newState == StatusBarState.KEYGUARD) {
+ mView.requestAnimateEverything();
+ }
+ }
+
+ @Override
+ public void onStateChanged(int newState) {
+ mView.setStatusBarState(newState);
+ }
+
+ @Override
+ public void onStatePostChange() {
+ mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(),
+ mLockscreenUserManager.isAnyProfilePublicMode());
+ mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
+ }
+ };
+
+ private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() {
+ @Override
+ public void onUserChanged(int userId) {
+ mView.setCurrentUserid(userId);
+ mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode());
+ }
+ };
+
+ private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
+ @Override
+ public void onMenuClicked(
+ View view, int x, int y, NotificationMenuRowPlugin.MenuItem item) {
+ if (!mAllowLongPress) {
+ return;
+ }
+ if (view instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ mMetricsLogger.write(row.getEntry().getSbn().getLogMaker()
+ .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
+ .setType(MetricsEvent.TYPE_ACTION)
+ );
+ }
+ mNotificationGutsManager.openGuts(view, x, y, item);
+ }
+
+ @Override
+ public void onMenuReset(View row) {
+ mView.onMenuReset(row);
+ }
+
+ @Override
+ public void onMenuShown(View row) {
+ if (row instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
+ mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker()
+ .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
+ .setType(MetricsEvent.TYPE_ACTION));
+ mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
+ mView.onMenuShown(row);
+ mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
+ false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
+
+ // Check to see if we want to go directly to the notification guts
+ NotificationMenuRowPlugin provider = notificationRow.getProvider();
+ if (provider.shouldShowGutsOnSnapOpen()) {
+ NotificationMenuRowPlugin.MenuItem item = provider.menuItemToExposeOnSnap();
+ if (item != null) {
+ Point origin = provider.getRevealAnimationOrigin();
+ mNotificationGutsManager.openGuts(row, origin.x, origin.y, item);
+ } else {
+ Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no "
+ + "menu item in menuItemtoExposeOnSnap. Skipping.");
+ }
+
+ // Close the menu row since we went directly to the guts
+ mView.resetExposedMenuView(false, true);
+ }
+ }
+ }
+ };
+
@Inject
public NotificationStackScrollLayoutController(
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
@@ -130,7 +246,14 @@ public class NotificationStackScrollLayoutController {
NotificationRoundnessManager notificationRoundnessManager,
TunerService tunerService,
DynamicPrivacyController dynamicPrivacyController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ SysuiStatusBarStateController statusBarStateController,
+ KeyguardMediaController keyguardMediaController,
+ KeyguardBypassController keyguardBypassController,
+ ZenModeController zenModeController,
+ SysuiColorExtractor colorExtractor,
+ NotificationLockscreenUserManager lockscreenUserManager,
+ MetricsLogger metricsLogger) {
mAllowLongPress = allowLongPress;
mNotificationGutsManager = notificationGutsManager;
mHeadsUpManager = headsUpManager;
@@ -138,19 +261,28 @@ public class NotificationStackScrollLayoutController {
mTunerService = tunerService;
mDynamicPrivacyController = dynamicPrivacyController;
mConfigurationController = configurationController;
+ mStatusBarStateController = statusBarStateController;
+ mKeyguardMediaController = keyguardMediaController;
+ mKeyguardBypassController = keyguardBypassController;
+ mZenModeController = zenModeController;
+ mColorExtractor = colorExtractor;
+ mLockscreenUserManager = lockscreenUserManager;
+ mMetricsLogger = metricsLogger;
}
public void attach(NotificationStackScrollLayout view) {
mView = view;
mView.setController(this);
-
- if (mAllowLongPress) {
- mView.setLongPressListener(mNotificationGutsManager::openGuts);
- }
+ mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled);
mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here?
mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener);
+ mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
+ mView.setCurrentUserid(mLockscreenUserManager.getCurrentUserId());
+
+ mView.setMenuEventListener(mMenuEventListener);
+
mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
@@ -165,6 +297,23 @@ public class NotificationStackScrollLayoutController {
Settings.Secure.NOTIFICATION_DISMISS_RTL,
Settings.Secure.NOTIFICATION_HISTORY_ENABLED);
+ mColorExtractor.addOnColorsChangedListener((colorExtractor, which) -> {
+ final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText();
+ mView.updateDecorViews(useDarkText);
+ });
+
+ mKeyguardMediaController.setVisibilityChangedListener(visible -> {
+ mView.setKeyguardMediaControllorVisible(visible);
+ if (visible) {
+ mView.generateAddAnimation(
+ mKeyguardMediaController.getView(), false /*fromMoreCard */);
+ } else {
+ mView.generateRemoveAnimation(mKeyguardMediaController.getView());
+ }
+ mView.requestChildrenUpdate();
+ return Unit.INSTANCE;
+ });
+
if (mView.isAttachedToWindow()) {
mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
}
@@ -484,7 +633,7 @@ public class NotificationStackScrollLayoutController {
}
public void updateEmptyShadeView(boolean visible) {
- mView.updateEmptyShadeView(visible);
+ mView.updateEmptyShadeView(visible, mZenModeController.areNotificationsHiddenInShade());
}
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
@@ -646,7 +795,7 @@ public class NotificationStackScrollLayoutController {
@Override
public void notifyGroupChildAdded(ExpandableView row) {
- mView.onViewAddedInternal(row);
+ mView.notifyGroupChildAdded(row);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index a6811c6b8ba4..78fcd82dc1f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -45,6 +45,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
import com.android.systemui.statusbar.NotificationMediaManager;
import libcore.io.IoUtils;
@@ -52,6 +53,7 @@ import libcore.io.IoUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
+import java.util.Optional;
import javax.inject.Inject;
@@ -68,6 +70,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
private final WallpaperManager mWallpaperManager;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Handler mH;
+ private final Optional<FaceAuthScreenBrightnessController> mFaceAuthScreenBrightnessController;
private boolean mCached;
private Bitmap mCache;
@@ -83,12 +86,14 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
KeyguardUpdateMonitor keyguardUpdateMonitor,
DumpManager dumpManager,
NotificationMediaManager mediaManager,
+ Optional<FaceAuthScreenBrightnessController> faceAuthScreenBrightnessController,
@Main Handler mainHandler) {
dumpManager.registerDumpable(getClass().getSimpleName(), this);
mWallpaperManager = wallpaperManager;
mCurrentUserId = ActivityManager.getCurrentUser();
mUpdateMonitor = keyguardUpdateMonitor;
mMediaManager = mediaManager;
+ mFaceAuthScreenBrightnessController = faceAuthScreenBrightnessController;
mH = mainHandler;
if (iWallpaperManager != null) {
@@ -128,6 +133,14 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
return LoaderResult.success(null);
}
+ Bitmap faceAuthWallpaper = null;
+ if (mFaceAuthScreenBrightnessController.isPresent()) {
+ faceAuthWallpaper = mFaceAuthScreenBrightnessController.get().getFaceAuthWallpaper();
+ if (faceAuthWallpaper != null) {
+ return LoaderResult.success(faceAuthWallpaper);
+ }
+ }
+
// Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK
// wallpaper.
final int lockWallpaperUserId =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 3c43a1777171..5abc42613fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -101,6 +101,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mCallbacks = Lists.newArrayList();
private final SysuiColorExtractor mColorExtractor;
+ private float mFaceAuthDisplayBrightness = LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
@Inject
public NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager,
@@ -232,6 +233,12 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mScreenBrightnessDoze = value / 255f;
}
+ @Override
+ public void setFaceAuthDisplayBrightness(float brightness) {
+ mFaceAuthDisplayBrightness = brightness;
+ apply(mCurrentState);
+ }
+
private void setKeyguardDark(boolean dark) {
int vis = mNotificationShadeView.getSystemUiVisibility();
if (dark) {
@@ -436,7 +443,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
if (state.mForceDozeBrightness) {
mLpChanged.screenBrightness = mScreenBrightnessDoze;
} else {
- mLpChanged.screenBrightness = LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
+ mLpChanged.screenBrightness = mFaceAuthDisplayBrightness;
}
}
@@ -701,6 +708,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
boolean mHeadsUpShowing;
boolean mForceCollapsed;
boolean mForceDozeBrightness;
+ int mFaceAuthDisplayBrightness;
boolean mForceUserActivity;
boolean mLaunchingActivity;
boolean mBackdropShowing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7ee501c681f9..777bf3f73480 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -50,6 +50,7 @@ import com.android.systemui.SystemUIFactory;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -68,6 +69,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Optional;
import javax.inject.Inject;
@@ -103,6 +105,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final ConfigurationController mConfigurationController;
private final NavigationModeController mNavigationModeController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final Optional<FaceAuthScreenBrightnessController> mFaceAuthScreenBrightnessController;
private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
@Override
public void onFullyShown() {
@@ -212,6 +215,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
DockManager dockManager,
NotificationShadeWindowController notificationShadeWindowController,
KeyguardStateController keyguardStateController,
+ Optional<FaceAuthScreenBrightnessController> faceAuthScreenBrightnessController,
NotificationMediaManager notificationMediaManager) {
mContext = context;
mViewMediatorCallback = callback;
@@ -224,6 +228,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mKeyguardUpdateManager = keyguardUpdateMonitor;
mStatusBarStateController = sysuiStatusBarStateController;
mDockManager = dockManager;
+ mFaceAuthScreenBrightnessController = faceAuthScreenBrightnessController;
}
@Override
@@ -248,6 +253,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
notificationPanelViewController.addExpansionListener(this);
mBypassController = bypassController;
mNotificationContainer = notificationContainer;
+ mFaceAuthScreenBrightnessController.ifPresent((it) -> {
+ View overlay = new View(mContext);
+ container.addView(overlay);
+ it.attach(overlay);
+ });
registerListeners();
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
index b7bc8c8fb7c4..302301d79c3a 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
@@ -24,7 +24,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.SystemServicesModule;
import com.android.systemui.dagger.SystemUIBinder;
import com.android.systemui.dagger.SystemUIModule;
-import com.android.systemui.onehanded.dagger.OneHandedModule;
import dagger.Subcomponent;
@@ -36,7 +35,6 @@ import dagger.Subcomponent;
DefaultComponentBinder.class,
DependencyProvider.class,
DependencyBinder.class,
- OneHandedModule.class,
SystemServicesModule.class,
SystemUIBinder.class,
SystemUIModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
index 3a2172ae0fae..66f8f74c7cab 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
@@ -25,13 +25,11 @@ import android.provider.Settings;
import java.util.concurrent.Executor;
-import javax.inject.Inject;
-
/**
* Wrapper around DeviceConfig useful for testing.
*/
public class DeviceConfigProxy {
- @Inject
+
public DeviceConfigProxy() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
index f22f59bee42f..bcfb2afeeda1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
@@ -4,8 +4,7 @@ import android.graphics.Rect
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.util.FloatingContentCoordinator.FloatingContent
-import java.util.*
-import javax.inject.Inject
+import java.util.HashMap
/** Tag for debug logging. */
private const val TAG = "FloatingCoordinator"
@@ -20,9 +19,9 @@ private const val TAG = "FloatingCoordinator"
* other content out of the way. [onContentRemoved] should be called when the content is removed or
* no longer visible.
*/
-@SysUISingleton
-class FloatingContentCoordinator @Inject constructor() {
+@SysUISingleton
+class FloatingContentCoordinator constructor() {
/**
* Represents a piece of floating content, such as PIP or the Bubbles stack. Provides methods
* that allow the [FloatingContentCoordinator] to determine the current location of the content,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index ac47660f3221..18cb7d3b3200 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -20,8 +20,13 @@ import android.content.Context;
import android.os.Handler;
import android.view.IWindowManager;
+import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.pip.PipUiEventLogger;
+import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
@@ -51,8 +56,33 @@ public class WMShellBaseModule {
@SysUISingleton
@Provides
+ static DeviceConfigProxy provideDeviceConfigProxy() {
+ return new DeviceConfigProxy();
+ }
+ @SysUISingleton
+ @Provides
+ static FloatingContentCoordinator provideFloatingContentCoordinator() {
+ return new FloatingContentCoordinator();
+ }
+
+ @SysUISingleton
+ @Provides
+ static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger) {
+ return new PipUiEventLogger(uiEventLogger);
+ }
+
+ @SysUISingleton
+ @Provides
static SystemWindows provideSystemWindows(DisplayController displayController,
IWindowManager wmService) {
return new SystemWindows(displayController, wmService);
}
+
+ @SysUISingleton
+ @Provides
+ public ShellTaskOrganizer provideShellTaskOrganizer() {
+ ShellTaskOrganizer organizer = new ShellTaskOrganizer();
+ organizer.registerOrganizer();
+ return organizer;
+ }
}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index c6331682f80c..db878458721e 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -80,6 +80,8 @@
android:excludeFromRecents="true"
/>
+ <activity android:name="com.android.systemui.screenshot.ScrollViewActivity"
+ android:exported="false" />
<provider
android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/res/values/strings.xml b/packages/SystemUI/tests/res/values/strings.xml
new file mode 100644
index 000000000000..b9f24c881813
--- /dev/null
+++ b/packages/SystemUI/tests/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <string name="test_content">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ultrices condimentum ultricies. Sed elementum at massa id sagittis. Nullam dictum massa lorem, nec ornare nunc pharetra vitae. Duis ultrices, felis eu condimentum congue, erat orci efficitur purus, ac rutrum odio lacus sed sapien. Suspendisse erat augue, eleifend eget auctor sagittis, porta eget nibh. Mauris pulvinar urna non justo condimentum, ut vehicula sapien finibus. Aliquam nibh magna, tincidunt ut viverra sed, placerat et turpis. Nam placerat, dui sed tincidunt consectetur, ante velit posuere mauris, tincidunt finibus velit lectus ac tortor. Cras eget lectus feugiat, porttitor velit nec, malesuada massa.</string>
+
+</resources>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
index 861c6207f5b0..690b9a7248be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
@@ -25,6 +25,7 @@ import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import org.junit.After
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -78,4 +79,15 @@ class ControlsFavoritePersistenceWrapperTest : SysuiTestCase() {
assertEquals(list, wrapper.readFavorites())
}
+
+ @Test
+ fun testSaveEmptyOnNonExistingFile() {
+ if (file.exists()) {
+ file.delete()
+ }
+
+ wrapper.storeFavorites(emptyList())
+
+ assertFalse(file.exists())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
new file mode 100644
index 000000000000..58cb0324c69a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.keyguard
+
+import android.animation.ValueAnimator
+import android.content.res.Resources
+import android.hardware.biometrics.BiometricSourceType
+import android.os.Handler
+import android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT
+import android.testing.AndroidTestingRunner
+import android.util.TypedValue
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Dumpable
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SystemSettings
+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.`when`
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+const val INITIAL_BRIGHTNESS = 0.5f
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class FaceAuthScreenBrightnessControllerTest : SysuiTestCase() {
+
+ @Mock
+ lateinit var whiteOverlay: View
+ @Mock
+ lateinit var dumpManager: DumpManager
+ @Mock
+ lateinit var resources: Resources
+ @Mock
+ lateinit var mainHandler: Handler
+ @Mock
+ lateinit var globalSettings: GlobalSettings
+ @Mock
+ lateinit var systemSettings: SystemSettings
+ @Mock
+ lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock
+ lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock
+ lateinit var animator: ValueAnimator
+ @Captor
+ lateinit var keyguardUpdateCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
+ lateinit var faceAuthScreenBrightnessController: FaceAuthScreenBrightnessController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ faceAuthScreenBrightnessController = object : FaceAuthScreenBrightnessController(
+ notificationShadeWindowController, keyguardUpdateMonitor, resources, globalSettings,
+ systemSettings, mainHandler, dumpManager) {
+ override fun createAnimator(start: Float, end: Float) = animator
+ }
+ `when`(systemSettings.getFloat(eq(SCREEN_BRIGHTNESS_FLOAT))).thenReturn(INITIAL_BRIGHTNESS)
+ faceAuthScreenBrightnessController.attach(whiteOverlay)
+ verify(keyguardUpdateMonitor).registerCallback(capture(keyguardUpdateCallback))
+ }
+
+ @Test
+ fun init_registersDumpManager() {
+ verify(dumpManager).registerDumpable(anyString(), any(Dumpable::class.java))
+ }
+
+ @Test
+ fun init_registersKeyguardCallback() {
+ verify(keyguardUpdateMonitor)
+ .registerCallback(any(KeyguardUpdateMonitorCallback::class.java))
+ }
+
+ @Test
+ fun onBiometricRunningChanged_animatesBrightness() {
+ clearInvocations(whiteOverlay)
+ keyguardUpdateCallback.value
+ .onBiometricRunningStateChanged(true, BiometricSourceType.FACE)
+ verify(whiteOverlay).visibility = eq(View.VISIBLE)
+ verify(animator).start()
+ }
+
+ @Test
+ fun faceAuthWallpaper_whenFaceIsDisabledForUser() {
+ faceAuthScreenBrightnessController.useFaceAuthWallpaper = true
+ faceAuthScreenBrightnessController.faceAuthWallpaper
+ verify(resources, never()).openRawResource(anyInt(), any(TypedValue::class.java))
+ }
+
+ @Test
+ fun faceAuthWallpaper_whenFaceFlagIsDisabled() {
+ faceAuthScreenBrightnessController.useFaceAuthWallpaper = true
+ faceAuthScreenBrightnessController.faceAuthWallpaper
+ verify(resources, never()).openRawResource(anyInt(), any(TypedValue::class.java))
+ }
+
+ @Test
+ fun faceAuthWallpaper_whenFaceIsEnabledForUser() {
+ faceAuthScreenBrightnessController.useFaceAuthWallpaper = true
+ `when`(keyguardUpdateMonitor.isFaceAuthEnabledForUser(anyInt())).thenReturn(true)
+ faceAuthScreenBrightnessController.faceAuthWallpaper
+ verify(resources).openRawResource(anyInt(), any(TypedValue::class.java))
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
index ca8f79d08ef0..da1ec9869d87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
@@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@@ -59,6 +60,7 @@ public class MediaPlayerDataTest : SysuiTestCase() {
}
@Test
+ @Ignore("Flaky")
fun switchPlayersPlaying() {
val playerIsPlaying1 = mock(MediaControlPanel::class.java)
whenever(playerIsPlaying1.isPlaying).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index 7a8e4f7e9b85..f38524369b46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -155,6 +155,22 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
}
@Test
+ fun testOnMediaDataLoaded_migratesKeys_noTimeoutExtension() {
+ // From not playing
+ mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ clearInvocations(mediaController)
+
+ // Migrate, still not playing
+ val playingState = mock(android.media.session.PlaybackState::class.java)
+ `when`(playingState.state).thenReturn(PlaybackState.STATE_PAUSED)
+ `when`(mediaController.playbackState).thenReturn(playingState)
+ mediaTimeoutListener.onMediaDataLoaded("NEWKEY", KEY, mediaData)
+
+ // Never cancels callback, or schedule another one
+ verify(cancellationRunnable, never()).run()
+ }
+
+ @Test
fun testOnPlaybackStateChanged_schedulesTimeout_whenPaused() {
// Assuming we're registered
testOnMediaDataLoaded_registersPlaybackListener()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java
index d7dba5fdf8c8..02d587d90655 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java
@@ -46,9 +46,9 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class OneHandedManagerImplTest extends OneHandedTestCase {
+public class OneHandedControllerTest extends OneHandedTestCase {
Display mDisplay;
- OneHandedManagerImpl mOneHandedManagerImpl;
+ OneHandedController mOneHandedController;
OneHandedTimeoutHandler mTimeoutHandler;
@Mock
@@ -70,7 +70,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mDisplay = mContext.getDisplay();
- mOneHandedManagerImpl = new OneHandedManagerImpl(
+ mOneHandedController = new OneHandedController(
getContext(),
mCommandQueue,
mMockDisplayController,
@@ -102,7 +102,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase {
@Test
public void testStartOneHanded() {
- mOneHandedManagerImpl.startOneHanded();
+ mOneHandedController.startOneHanded();
verify(mMockDisplayAreaOrganizer).scheduleOffset(anyInt(), anyInt());
}
@@ -110,7 +110,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase {
@Test
public void testStopOneHanded() {
when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
- mOneHandedManagerImpl.stopOneHanded();
+ mOneHandedController.stopOneHanded();
verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt());
}
@@ -122,7 +122,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase {
@Test
public void testStopOneHanded_shouldRemoveTimer() {
- mOneHandedManagerImpl.stopOneHanded();
+ mOneHandedController.stopOneHanded();
verify(mTimeoutHandler).removeTimer();
}
@@ -130,7 +130,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase {
@Test
public void testUpdateIsEnabled() {
final boolean enabled = true;
- mOneHandedManagerImpl.setOneHandedEnabled(enabled);
+ mOneHandedController.setOneHandedEnabled(enabled);
verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled);
}
@@ -138,7 +138,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase {
@Test
public void testUpdateSwipeToNotificationIsEnabled() {
final boolean enabled = true;
- mOneHandedManagerImpl.setSwipeToNotificationEnabled(enabled);
+ mOneHandedController.setSwipeToNotificationEnabled(enabled);
verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
index 95a230f6511c..756382a6c630 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
@@ -30,8 +30,8 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
@@ -48,7 +48,7 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase {
OneHandedTouchHandler mTouchHandler;
OneHandedTutorialHandler mTutorialHandler;
OneHandedGestureHandler mGestureHandler;
- OneHandedManagerImpl mOneHandedManagerImpl;
+ OneHandedController mOneHandedController;
@Mock
CommandQueue mCommandQueue;
@Mock
@@ -66,7 +66,7 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase {
mTutorialHandler = new OneHandedTutorialHandler(mContext);
mGestureHandler = Mockito.spy(new OneHandedGestureHandler(
mContext, mMockDisplayController, mMockNavigationModeController));
- mOneHandedManagerImpl = new OneHandedManagerImpl(
+ mOneHandedController = new OneHandedController(
getContext(),
mCommandQueue,
mMockDisplayController,
@@ -93,15 +93,15 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase {
public void testReceiveNewConfig_whenSetOneHandedEnabled() {
// 1st called at init
verify(mGestureHandler).onOneHandedEnabled(true);
- mOneHandedManagerImpl.setOneHandedEnabled(true);
+ mOneHandedController.setOneHandedEnabled(true);
// 2nd called by setOneHandedEnabled()
verify(mGestureHandler, times(2)).onOneHandedEnabled(true);
}
@Test
public void testOneHandedDisabled_shouldDisposeInputChannel() {
- mOneHandedManagerImpl.setOneHandedEnabled(false);
- mOneHandedManagerImpl.setSwipeToNotificationEnabled(false);
+ mOneHandedController.setOneHandedEnabled(false);
+ mOneHandedController.setSwipeToNotificationEnabled(false);
assertThat(mGestureHandler.mInputMonitor).isNull();
assertThat(mGestureHandler.mInputEventReceiver).isNull();
@@ -111,7 +111,7 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase {
public void testChangeNavBarTo2Button_shouldDisposeInputChannel() {
// 1st called at init
verify(mGestureHandler).onOneHandedEnabled(true);
- mOneHandedManagerImpl.setOneHandedEnabled(true);
+ mOneHandedController.setOneHandedEnabled(true);
// 2nd called by setOneHandedEnabled()
verify(mGestureHandler, times(2)).onOneHandedEnabled(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
index 8ae632dd5a47..3c3ace052e47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
@@ -28,8 +28,8 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
@@ -46,7 +46,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase {
OneHandedTouchHandler mTouchHandler;
OneHandedTutorialHandler mTutorialHandler;
OneHandedGestureHandler mGestureHandler;
- OneHandedManagerImpl mOneHandedManagerImpl;
+ OneHandedController mOneHandedController;
@Mock
CommandQueue mCommandQueue;
@Mock
@@ -64,7 +64,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase {
mTouchHandler = Mockito.spy(new OneHandedTouchHandler());
mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
mMockNavigationModeController);
- mOneHandedManagerImpl = new OneHandedManagerImpl(
+ mOneHandedController = new OneHandedController(
getContext(),
mCommandQueue,
mMockDisplayController,
@@ -88,14 +88,14 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase {
@Test
public void testOneHandedDisabled_shouldDisposeInputChannel() {
- mOneHandedManagerImpl.setOneHandedEnabled(false);
+ mOneHandedController.setOneHandedEnabled(false);
assertThat(mTouchHandler.mInputMonitor).isNull();
assertThat(mTouchHandler.mInputEventReceiver).isNull();
}
@Test
public void testOneHandedEnabled_monitorInputChannel() {
- mOneHandedManagerImpl.setOneHandedEnabled(true);
+ mOneHandedController.setOneHandedEnabled(true);
assertThat(mTouchHandler.mInputMonitor).isNotNull();
assertThat(mTouchHandler.mInputEventReceiver).isNotNull();
}
@@ -104,7 +104,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase {
public void testReceiveNewConfig_whenSetOneHandedEnabled() {
// 1st called at init
verify(mTouchHandler).onOneHandedEnabled(true);
- mOneHandedManagerImpl.setOneHandedEnabled(true);
+ mOneHandedController.setOneHandedEnabled(true);
// 2nd called by setOneHandedEnabled()
verify(mTouchHandler, times(2)).onOneHandedEnabled(true);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
index c75a8d2f5454..1bffbf7eb8dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
@@ -24,8 +24,8 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
@@ -42,7 +42,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
OneHandedTouchHandler mTouchHandler;
OneHandedTutorialHandler mTutorialHandler;
OneHandedGestureHandler mGestureHandler;
- OneHandedManagerImpl mOneHandedManagerImpl;
+ OneHandedController mOneHandedController;
@Mock
CommandQueue mCommandQueue;
@Mock
@@ -61,7 +61,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
mTutorialHandler = Mockito.spy(new OneHandedTutorialHandler(mContext));
mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
mMockNavigationModeController);
- mOneHandedManagerImpl = new OneHandedManagerImpl(
+ mOneHandedController = new OneHandedController(
getContext(),
mCommandQueue,
mMockDisplayController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
index f0e713e42046..ae3df5db30bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
@@ -48,7 +48,7 @@ public class OneHandedUITest extends OneHandedTestCase {
OneHandedUI mOneHandedUI;
ScreenLifecycle mScreenLifecycle;
@Mock
- OneHandedManagerImpl mMockOneHandedManagerImpl;
+ OneHandedController mOneHandedController;
@Mock
OneHandedTimeoutHandler mMockTimeoutHandler;
@@ -59,7 +59,7 @@ public class OneHandedUITest extends OneHandedTestCase {
mScreenLifecycle = new ScreenLifecycle();
mOneHandedUI = new OneHandedUI(mContext,
mCommandQueue,
- mMockOneHandedManagerImpl,
+ mOneHandedController,
mScreenLifecycle);
mOneHandedUI.start();
mKeyguardUpdateMonitor = mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
@@ -74,14 +74,14 @@ public class OneHandedUITest extends OneHandedTestCase {
public void testStartOneHanded() {
mOneHandedUI.startOneHanded();
- verify(mMockOneHandedManagerImpl).startOneHanded();
+ verify(mOneHandedController).startOneHanded();
}
@Test
public void testStopOneHanded() {
mOneHandedUI.stopOneHanded();
- verify(mMockOneHandedManagerImpl).stopOneHanded();
+ verify(mOneHandedController).stopOneHanded();
}
@Test
@@ -89,7 +89,7 @@ public class OneHandedUITest extends OneHandedTestCase {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.TAPS_APP_TO_EXIT, 1);
- verify(mMockOneHandedManagerImpl).setTaskChangeToExit(true);
+ verify(mOneHandedController).setTaskChangeToExit(true);
}
@Test
@@ -97,7 +97,7 @@ public class OneHandedUITest extends OneHandedTestCase {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
- verify(mMockOneHandedManagerImpl).setOneHandedEnabled(true);
+ verify(mOneHandedController).setOneHandedEnabled(true);
}
@Test
@@ -115,7 +115,7 @@ public class OneHandedUITest extends OneHandedTestCase {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1);
- verify(mMockOneHandedManagerImpl).setSwipeToNotificationEnabled(true);
+ verify(mOneHandedController).setSwipeToNotificationEnabled(true);
}
@Ignore("Clarifying do not receive callback")
@@ -123,14 +123,14 @@ public class OneHandedUITest extends OneHandedTestCase {
public void testKeyguardBouncerShowing_shouldStopOneHanded() {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
- verify(mMockOneHandedManagerImpl).stopOneHanded();
+ verify(mOneHandedController).stopOneHanded();
}
@Test
public void testScreenTurningOff_shouldStopOneHanded() {
mScreenLifecycle.dispatchScreenTurningOff();
- verify(mMockOneHandedManagerImpl).stopOneHanded();
+ verify(mOneHandedController).stopOneHanded();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index e9d2b73182e0..cdb177096f11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -19,7 +19,6 @@ package com.android.systemui.pip;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
import android.content.ComponentName;
import android.graphics.Rect;
@@ -33,7 +32,6 @@ import android.view.Gravity;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
@@ -65,8 +63,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
initializeMockResources();
- mPipBoundsHandler = new PipBoundsHandler(mContext, new PipSnapAlgorithm(mContext),
- mock(DisplayController.class));
+ mPipBoundsHandler = new PipBoundsHandler(mContext);
mTestComponentName1 = new ComponentName(mContext, "component1");
mTestComponentName2 = new ComponentName(mContext, "component2");
@@ -113,7 +110,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
res.addOverride(com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio,
newDefaultAspectRatio);
- mPipBoundsHandler.onConfigurationChanged();
+ mPipBoundsHandler.onConfigurationChanged(mContext);
assertEquals("Default aspect ratio should be reloaded",
mPipBoundsHandler.getDefaultAspectRatio(), newDefaultAspectRatio,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
index 9f67722041aa..c8d4aca90519 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
@@ -72,9 +72,6 @@ public class PipTouchHandlerTest extends SysuiTestCase {
private InputConsumerController mInputConsumerController;
@Mock
- private PipBoundsHandler mPipBoundsHandler;
-
- @Mock
private PipTaskOrganizer mPipTaskOrganizer;
@Mock
@@ -89,6 +86,7 @@ public class PipTouchHandlerTest extends SysuiTestCase {
@Mock
private PipUiEventLogger mPipUiEventLogger;
+ private PipBoundsHandler mPipBoundsHandler;
private PipSnapAlgorithm mPipSnapAlgorithm;
private PipMotionHelper mMotionHelper;
private PipResizeGestureHandler mPipResizeGestureHandler;
@@ -104,11 +102,13 @@ public class PipTouchHandlerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mPipBoundsHandler = new PipBoundsHandler(mContext);
+ mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm();
mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager,
mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler,
- mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy,
- mPipSnapAlgorithm, mSysUiState, mPipUiEventLogger);
+ mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy, mSysUiState,
+ mPipUiEventLogger);
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
mPipTouchHandler.setPipMotionHelper(mMotionHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
new file mode 100644
index 000000000000..e7ef64e6adad
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.screenshot;
+
+import static org.junit.Assert.fail;
+
+import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.util.Log;
+import android.view.Display;
+import android.view.IScrollCaptureClient;
+import android.view.IScrollCaptureController;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests the of internal framework Scroll Capture API from SystemUI.
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class ScrollCaptureTest extends SysuiTestCase {
+ private static final String TAG = "ScrollCaptureTest";
+
+ /**
+ * Verifies that a request traverses from SystemUI, to WindowManager and to the app process and
+ * is returned without error. Device must be unlocked.
+ */
+ @Test
+ public void testBasicOperation() throws InterruptedException {
+ IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+
+ // Start an activity to be on top that will be targeted
+ InstrumentationRegistry.getInstrumentation().startActivitySync(
+ new Intent(mContext, ScrollViewActivity.class).addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK));
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ try {
+ wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1,
+ new IScrollCaptureController.Stub() {
+ @Override
+ public void onClientConnected(
+ IScrollCaptureClient client, Rect scrollBounds,
+ Point positionInWindow) {
+ Log.d(TAG,
+ "client connected: " + client + "[scrollBounds= " + scrollBounds
+ + ", positionInWindow=" + positionInWindow + "]");
+ latch.countDown();
+ }
+
+ @Override
+ public void onClientUnavailable() {
+ }
+
+ @Override
+ public void onCaptureStarted() {
+ }
+
+ @Override
+ public void onCaptureBufferSent(long frameNumber, Rect capturedArea) {
+ }
+
+ @Override
+ public void onConnectionClosed() {
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "request failed", e);
+ fail("caught remote exception " + e);
+ }
+ latch.await(1000, TimeUnit.MILLISECONDS);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
new file mode 100644
index 000000000000..bd3725942eca
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
@@ -0,0 +1,42 @@
+/*
+ * 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.screenshot;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.TypedValue;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+public class ScrollViewActivity extends Activity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ScrollView scrollView = new ScrollView(this);
+ LinearLayout linearLayout = new LinearLayout(this);
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+ TextView text = new TextView(this);
+ text.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 40);
+ text.setText(com.android.systemui.R.string.test_content);
+ linearLayout.addView(text);
+ scrollView.addView(linearLayout);
+ setContentView(scrollView);
+ }
+}
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 1523653dec3c..3dc29a1ae4d1 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
@@ -53,7 +53,6 @@ public class NotificationEntryBuilder {
/* ListEntry properties */
private GroupEntry mParent;
- private int mSection = -1;
/* If set, use this creation time instead of mClock.uptimeMillis */
private long mCreationTime = -1;
@@ -68,7 +67,6 @@ public class NotificationEntryBuilder {
mRankingBuilder = new RankingBuilder(source.getRanking());
mParent = source.getParent();
- mSection = source.getSection();
mCreationTime = source.getCreationTime();
}
@@ -104,7 +102,6 @@ public class NotificationEntryBuilder {
/* ListEntry properties */
entry.setParent(mParent);
- entry.getAttachState().setSectionIndex(mSection);
return entry;
}
@@ -117,14 +114,6 @@ public class NotificationEntryBuilder {
}
/**
- * Sets the section.
- */
- public NotificationEntryBuilder setSection(int section) {
- mSection = section;
- return this;
- }
-
- /**
* Sets the SBN directly. If set, causes all calls to delegated SbnBuilder methods to be
* ignored.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 6fa5055c875d..8acb705c744d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -35,6 +35,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static java.util.Collections.singletonList;
+
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -46,6 +48,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.NotificationInteractionTracker;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder.OnRenderListListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
@@ -54,7 +57,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeL
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.util.time.FakeSystemClock;
@@ -71,7 +74,6 @@ import org.mockito.Spy;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -496,7 +498,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
assertTrue(entry.hasFinishedInitialization());
// WHEN the pipeline is kicked off
- mReadyForBuildListener.onBuildList(Arrays.asList(entry));
+ mReadyForBuildListener.onBuildList(singletonList(entry));
// THEN the entry's initialization time is reset
assertFalse(entry.hasFinishedInitialization());
@@ -609,13 +611,18 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// GIVEN a filter that removes all PACKAGE_4 notifs and sections that divide
// notifs based on package name
mListBuilder.addPreGroupFilter(new PackageFilter(PACKAGE_4));
- final NotifSection pkg1Section = spy(new PackageSection(PACKAGE_1));
- final NotifSection pkg2Section = spy(new PackageSection(PACKAGE_2));
+ final NotifSectioner pkg1Sectioner = spy(new PackageSectioner(PACKAGE_1));
+ final NotifSectioner pkg2Sectioner = spy(new PackageSectioner(PACKAGE_2));
// NOTE: no package 3 section explicitly added, so notifs with package 3 will get set by
// ShadeListBuilder's sDefaultSection which will demote it to the last section
- final NotifSection pkg4Section = spy(new PackageSection(PACKAGE_4));
- final NotifSection pkg5Section = spy(new PackageSection(PACKAGE_5));
- mListBuilder.setSections(Arrays.asList(pkg1Section, pkg2Section, pkg4Section, pkg5Section));
+ final NotifSectioner pkg4Sectioner = spy(new PackageSectioner(PACKAGE_4));
+ final NotifSectioner pkg5Sectioner = spy(new PackageSectioner(PACKAGE_5));
+ mListBuilder.setSectioners(
+ Arrays.asList(pkg1Sectioner, pkg2Sectioner, pkg4Sectioner, pkg5Sectioner));
+
+ final NotifSection pkg1Section = new NotifSection(pkg1Sectioner, 0);
+ final NotifSection pkg2Section = new NotifSection(pkg2Sectioner, 1);
+ final NotifSection pkg5Section = new NotifSection(pkg5Sectioner, 3);
// WHEN we build a list with different packages
addNotif(0, PACKAGE_4);
@@ -648,72 +655,61 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// THEN the first section (pkg1Section) is called on all top level elements (but
// no children and no entries that were filtered out)
- verify(pkg1Section).isInSection(mEntrySet.get(1));
- verify(pkg1Section).isInSection(mEntrySet.get(2));
- verify(pkg1Section).isInSection(mEntrySet.get(3));
- verify(pkg1Section).isInSection(mEntrySet.get(7));
- verify(pkg1Section).isInSection(mEntrySet.get(8));
- verify(pkg1Section).isInSection(mEntrySet.get(9));
- verify(pkg1Section).isInSection(mBuiltList.get(3));
-
- verify(pkg1Section, never()).isInSection(mEntrySet.get(0));
- verify(pkg1Section, never()).isInSection(mEntrySet.get(4));
- verify(pkg1Section, never()).isInSection(mEntrySet.get(5));
- verify(pkg1Section, never()).isInSection(mEntrySet.get(6));
- verify(pkg1Section, never()).isInSection(mEntrySet.get(10));
+ verify(pkg1Sectioner).isInSection(mEntrySet.get(1));
+ verify(pkg1Sectioner).isInSection(mEntrySet.get(2));
+ verify(pkg1Sectioner).isInSection(mEntrySet.get(3));
+ verify(pkg1Sectioner).isInSection(mEntrySet.get(7));
+ verify(pkg1Sectioner).isInSection(mEntrySet.get(8));
+ verify(pkg1Sectioner).isInSection(mEntrySet.get(9));
+ verify(pkg1Sectioner).isInSection(mBuiltList.get(3));
+
+ verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(0));
+ verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(4));
+ verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(5));
+ verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(6));
+ verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(10));
// THEN the last section (pkg5Section) is not called on any of the entries that were
// filtered or already in a section
- verify(pkg5Section, never()).isInSection(mEntrySet.get(0));
- verify(pkg5Section, never()).isInSection(mEntrySet.get(1));
- verify(pkg5Section, never()).isInSection(mEntrySet.get(2));
- verify(pkg5Section, never()).isInSection(mEntrySet.get(4));
- verify(pkg5Section, never()).isInSection(mEntrySet.get(5));
- verify(pkg5Section, never()).isInSection(mEntrySet.get(6));
- verify(pkg5Section, never()).isInSection(mEntrySet.get(7));
- verify(pkg5Section, never()).isInSection(mEntrySet.get(8));
- verify(pkg5Section, never()).isInSection(mEntrySet.get(10));
-
- verify(pkg5Section).isInSection(mEntrySet.get(3));
- verify(pkg5Section).isInSection(mEntrySet.get(9));
+ verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(0));
+ verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(1));
+ verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(2));
+ verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(4));
+ verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(5));
+ verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(6));
+ verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(7));
+ verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(8));
+ verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(10));
+
+ verify(pkg5Sectioner).isInSection(mEntrySet.get(3));
+ verify(pkg5Sectioner).isInSection(mEntrySet.get(9));
// THEN the correct section is assigned for entries in pkg1Section
- assertEquals(pkg1Section, mEntrySet.get(2).getNotifSection());
- assertEquals(0, mEntrySet.get(2).getSection());
- assertEquals(pkg1Section, mEntrySet.get(7).getNotifSection());
- assertEquals(0, mEntrySet.get(7).getSection());
+ assertEquals(pkg1Section, mEntrySet.get(2).getSection());
+ assertEquals(pkg1Section, mEntrySet.get(7).getSection());
// THEN the correct section is assigned for entries in pkg2Section
- assertEquals(pkg2Section, mEntrySet.get(1).getNotifSection());
- assertEquals(1, mEntrySet.get(1).getSection());
- assertEquals(pkg2Section, mEntrySet.get(8).getNotifSection());
- assertEquals(1, mEntrySet.get(8).getSection());
- assertEquals(pkg2Section, mBuiltList.get(3).getNotifSection());
- assertEquals(1, mBuiltList.get(3).getSection());
+ assertEquals(pkg2Section, mEntrySet.get(1).getSection());
+ assertEquals(pkg2Section, mEntrySet.get(8).getSection());
+ assertEquals(pkg2Section, mBuiltList.get(3).getSection());
// THEN no section was assigned to entries in pkg4Section (since they were filtered)
- assertEquals(null, mEntrySet.get(0).getNotifSection());
- assertEquals(-1, mEntrySet.get(0).getSection());
- assertEquals(null, mEntrySet.get(10).getNotifSection());
- assertEquals(-1, mEntrySet.get(10).getSection());
-
+ assertNull(mEntrySet.get(0).getSection());
+ assertNull(mEntrySet.get(10).getSection());
// THEN the correct section is assigned for entries in pkg5Section
- assertEquals(pkg5Section, mEntrySet.get(9).getNotifSection());
- assertEquals(3, mEntrySet.get(9).getSection());
+ assertEquals(pkg5Section, mEntrySet.get(9).getSection());
// THEN the children entries are assigned the same section as its parent
- assertEquals(mBuiltList.get(3).getNotifSection(), child(5).entry.getNotifSection());
assertEquals(mBuiltList.get(3).getSection(), child(5).entry.getSection());
- assertEquals(mBuiltList.get(3).getNotifSection(), child(6).entry.getNotifSection());
assertEquals(mBuiltList.get(3).getSection(), child(6).entry.getSection());
}
@Test
public void testNotifUsesDefaultSection() {
// GIVEN a Section for Package2
- final NotifSection pkg2Section = spy(new PackageSection(PACKAGE_2));
- mListBuilder.setSections(Arrays.asList(pkg2Section));
+ final NotifSectioner pkg2Section = spy(new PackageSectioner(PACKAGE_2));
+ mListBuilder.setSectioners(singletonList(pkg2Section));
// WHEN we build a list with pkg1 and pkg2 packages
addNotif(0, PACKAGE_1);
@@ -727,8 +723,8 @@ public class ShadeListBuilderTest extends SysuiTestCase {
);
// THEN the entry that didn't have an explicit section gets assigned the DefaultSection
- assertEquals(1, notif(0).entry.getSection());
- assertNotNull(notif(0).entry.getNotifSection());
+ assertNotNull(notif(0).entry.getSection());
+ assertEquals(1, notif(0).entry.getSectionIndex());
}
@Test
@@ -763,15 +759,15 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// GIVEN a bunch of registered listeners and pluggables
NotifFilter preGroupFilter = spy(new PackageFilter(PACKAGE_1));
NotifPromoter promoter = spy(new IdPromoter(3));
- NotifSection section = spy(new PackageSection(PACKAGE_1));
+ NotifSectioner section = spy(new PackageSectioner(PACKAGE_1));
NotifComparator comparator = spy(new HypeComparator(PACKAGE_4));
NotifFilter preRenderFilter = spy(new PackageFilter(PACKAGE_5));
mListBuilder.addPreGroupFilter(preGroupFilter);
mListBuilder.addOnBeforeTransformGroupsListener(mOnBeforeTransformGroupsListener);
mListBuilder.addPromoter(promoter);
mListBuilder.addOnBeforeSortListener(mOnBeforeSortListener);
- mListBuilder.setComparators(Collections.singletonList(comparator));
- mListBuilder.setSections(Arrays.asList(section));
+ mListBuilder.setComparators(singletonList(comparator));
+ mListBuilder.setSectioners(singletonList(section));
mListBuilder.addOnBeforeFinalizeFilterListener(mOnBeforeFinalizeFilterListener);
mListBuilder.addFinalizeFilter(preRenderFilter);
mListBuilder.addOnBeforeRenderListListener(mOnBeforeRenderListListener);
@@ -821,13 +817,13 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// GIVEN a variety of pluggables
NotifFilter packageFilter = new PackageFilter(PACKAGE_1);
NotifPromoter idPromoter = new IdPromoter(4);
- NotifSection section = new PackageSection(PACKAGE_1);
+ NotifSectioner section = new PackageSectioner(PACKAGE_1);
NotifComparator hypeComparator = new HypeComparator(PACKAGE_2);
mListBuilder.addPreGroupFilter(packageFilter);
mListBuilder.addPromoter(idPromoter);
- mListBuilder.setSections(Arrays.asList(section));
- mListBuilder.setComparators(Collections.singletonList(hypeComparator));
+ mListBuilder.setSectioners(singletonList(section));
+ mListBuilder.setComparators(singletonList(hypeComparator));
// GIVEN a set of random notifs
addNotif(0, PACKAGE_1);
@@ -973,7 +969,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
RecordingOnBeforeSortListener listener =
new RecordingOnBeforeSortListener();
mListBuilder.addOnBeforeSortListener(listener);
- mListBuilder.setComparators(Arrays.asList(new HypeComparator(PACKAGE_3)));
+ mListBuilder.setComparators(singletonList(new HypeComparator(PACKAGE_3)));
// GIVEN some new notifs out of order
addNotif(0, PACKAGE_1);
@@ -1093,7 +1089,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
NotifComparator comparator = new HypeComparator(PACKAGE_5);
OnBeforeRenderListListener listener =
(list) -> comparator.invalidateList();
- mListBuilder.setComparators(Collections.singletonList(comparator));
+ mListBuilder.setComparators(singletonList(comparator));
mListBuilder.addOnBeforeRenderListListener(listener);
// WHEN we try to run the pipeline and the comparator is invalidated
@@ -1420,10 +1416,10 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
/** Represents a section for the passed pkg */
- private static class PackageSection extends NotifSection {
+ private static class PackageSectioner extends NotifSectioner {
private final String mPackage;
- PackageSection(String pkg) {
+ PackageSectioner(String pkg) {
super("PackageSection_" + pkg);
mPackage = pkg;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
index ce8ce2e39bcc..639e791cbf23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
@@ -21,11 +21,9 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -37,7 +35,6 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -48,8 +45,7 @@ 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.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -61,8 +57,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.List;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -78,7 +72,7 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
private AppOpsCoordinator mAppOpsCoordinator;
private NotifFilter mForegroundFilter;
private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
- private NotifSection mFgsSection;
+ private NotifSectioner mFgsSection;
private FakeSystemClock mClock = new FakeSystemClock();
private FakeExecutor mExecutor = new FakeExecutor(mClock);
@@ -111,7 +105,7 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
lifetimeExtenderCaptor.capture());
mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue();
- mFgsSection = mAppOpsCoordinator.getSection();
+ mFgsSection = mAppOpsCoordinator.getSectioner();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index be5c8a846afb..c49393d2ed34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -25,7 +25,7 @@ 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.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
import org.junit.Assert.assertFalse
@@ -45,7 +45,7 @@ import org.mockito.Mockito.`when` as whenever
class ConversationCoordinatorTest : SysuiTestCase() {
// captured listeners and pluggables:
private lateinit var promoter: NotifPromoter
- private lateinit var peopleSection: NotifSection
+ private lateinit var peopleSectioner: NotifSectioner
@Mock
private lateinit var pipeline: NotifPipeline
@@ -70,7 +70,7 @@ class ConversationCoordinatorTest : SysuiTestCase() {
verify(pipeline).addPromoter(notifPromoterCaptor.capture())
promoter = notifPromoterCaptor.value
- peopleSection = coordinator.getSection()
+ peopleSectioner = coordinator.sectioner
entry = NotificationEntryBuilder().setChannel(channel).build()
}
@@ -88,7 +88,7 @@ class ConversationCoordinatorTest : SysuiTestCase() {
entry.sbn, entry.ranking)).thenReturn(TYPE_PERSON)
// only put people notifications in this section
- assertTrue(peopleSection.isInSection(entry))
- assertFalse(peopleSection.isInSection(NotificationEntryBuilder().build()))
+ assertTrue(peopleSectioner.isInSection(entry))
+ assertFalse(peopleSectioner.isInSection(NotificationEntryBuilder().build()))
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
index 730481afe638..fa992a5d5dbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
@@ -36,7 +36,7 @@ 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.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
@@ -64,7 +64,7 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
private NotifPromoter mNotifPromoter;
private NotifLifetimeExtender mNotifLifetimeExtender;
private OnHeadsUpChangedListener mOnHeadsUpChangedListener;
- private NotifSection mNotifSection;
+ private NotifSectioner mNotifSectioner;
@Mock private NotifPipeline mNotifPipeline;
@Mock private HeadsUpManager mHeadsUpManager;
@@ -111,7 +111,7 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue();
mOnHeadsUpChangedListener = headsUpChangedListenerCaptor.getValue();
- mNotifSection = mCoordinator.getSection();
+ mNotifSectioner = mCoordinator.getSectioner();
mNotifLifetimeExtender.setCallback(mEndLifetimeExtension);
mEntry = new NotificationEntryBuilder().build();
}
@@ -132,8 +132,8 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
setCurrentHUN(mEntry);
// THEN only section the current HUN, mEntry
- assertTrue(mNotifSection.isInSection(mEntry));
- assertFalse(mNotifSection.isInSection(new NotificationEntryBuilder().build()));
+ assertTrue(mNotifSectioner.isInSection(mEntry));
+ assertFalse(mNotifSectioner.isInSection(new NotificationEntryBuilder().build()));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index 5f10f38b2ee8..3a7d28ab56ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -36,7 +36,7 @@ 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.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import org.junit.Before;
@@ -61,8 +61,8 @@ public class RankingCoordinatorTest extends SysuiTestCase {
private NotifFilter mCapturedSuspendedFilter;
private NotifFilter mCapturedDozingFilter;
- private NotifSection mAlertingSection;
- private NotifSection mSilentSection;
+ private NotifSectioner mAlertingSectioner;
+ private NotifSectioner mSilentSectioner;
@Before
public void setup() {
@@ -76,8 +76,8 @@ public class RankingCoordinatorTest extends SysuiTestCase {
mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
- mAlertingSection = rankingCoordinator.getAlertingSection();
- mSilentSection = rankingCoordinator.getSilentSection();
+ mAlertingSectioner = rankingCoordinator.getAlertingSectioner();
+ mSilentSectioner = rankingCoordinator.getSilentSectioner();
}
@Test
@@ -146,8 +146,8 @@ public class RankingCoordinatorTest extends SysuiTestCase {
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true);
// THEN entry is in the alerting section
- assertTrue(mAlertingSection.isInSection(mEntry));
- assertFalse(mSilentSection.isInSection(mEntry));
+ assertTrue(mAlertingSectioner.isInSection(mEntry));
+ assertFalse(mSilentSectioner.isInSection(mEntry));
}
@Test
@@ -156,8 +156,8 @@ public class RankingCoordinatorTest extends SysuiTestCase {
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
// THEN entry is in the silent section
- assertFalse(mAlertingSection.isInSection(mEntry));
- assertTrue(mSilentSection.isInSection(mEntry));
+ assertFalse(mAlertingSectioner.isInSection(mEntry));
+ assertTrue(mSilentSectioner.isInSection(mEntry));
}
private RankingBuilder getRankingForUnfilteredNotif() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 9a6674e165e4..bca7b312ff15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -24,9 +24,7 @@ import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
@@ -47,19 +45,15 @@ import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -87,14 +81,13 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.leak.LeakDetector;
import org.junit.After;
@@ -103,7 +96,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -135,14 +127,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private MetricsLogger mMetricsLogger;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
- @Mock private KeyguardBypassController mKeyguardBypassController;
- @Mock private KeyguardMediaController mKeyguardMediaController;
- @Mock private ZenModeController mZenModeController;
+ @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
@Mock private NotificationSectionsManager mNotificationSectionsManager;
@Mock private NotificationSection mNotificationSection;
- @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private FeatureFlags mFeatureFlags;
- private UserChangedListener mUserChangedListener;
+ @Mock private SysuiStatusBarStateController mStatusBarStateController;
private NotificationEntryManager mEntryManager;
private int mOriginalInterruptionModelSetting;
private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
@@ -172,8 +161,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mDependency.injectMockDependency(ShadeController.class);
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
- ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor
- .forClass(UserChangedListener.class);
mEntryManager = new NotificationEntryManager(
mock(NotificationEntryManagerLogger.class),
mock(NotificationGroupManager.class),
@@ -211,17 +198,15 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
// which refer to members of NotificationStackScrollLayout. The spy
// holds a copy of the CUT's instances of these KeyguardBypassController, so they still
// refer to the CUT's member variables, not the spy's member variables.
- mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null,
+ mStackScrollerInternal = new NotificationStackScrollLayout(
+ getContext(),
+ null,
mNotificationRoundnessManager,
mock(DynamicPrivacyController.class),
- mock(SysuiStatusBarStateController.class),
+ mStatusBarStateController,
mHeadsUpManager,
- mKeyguardBypassController,
- mKeyguardMediaController,
new FalsingManagerFake(),
- mLockscreenUserManager,
mock(NotificationGutsManager.class),
- mZenModeController,
mNotificationSectionsManager,
mock(ForegroundServiceSectionController.class),
mock(ForegroundServiceDismissalFeatureController.class),
@@ -231,8 +216,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mock(NotifCollection.class),
mUiEventLoggerFake
);
- verify(mLockscreenUserManager).addUserChangedListener(userChangedCaptor.capture());
- mUserChangedListener = userChangedCaptor.getValue();
+ mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider);
mStackScroller = spy(mStackScrollerInternal);
mStackScroller.setShelfController(notificationShelfController);
mStackScroller.setStatusBar(mBar);
@@ -269,9 +253,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
public void updateEmptyView_dndSuppressing() {
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true);
+ mStackScroller.updateEmptyShadeView(true, true);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
}
@@ -280,9 +263,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
public void updateEmptyView_dndNotSuppressing() {
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
- mStackScroller.updateEmptyShadeView(true);
+ mStackScroller.updateEmptyShadeView(true, false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
}
@@ -291,12 +273,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
public void updateEmptyView_noNotificationsToDndSuppressing() {
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
- mStackScroller.updateEmptyShadeView(true);
+ mStackScroller.updateEmptyShadeView(true, false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
- when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true);
+ mStackScroller.updateEmptyShadeView(true, true);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
}
@@ -312,12 +292,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
- public void testOnStatePostChange_verifyIfProfileIsPublic() {
- mUserChangedListener.onUserChanged(0);
- verify(mLockscreenUserManager).isAnyProfilePublicMode();
- }
-
- @Test
public void manageNotifications_visible() {
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
@@ -473,61 +447,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
assertNull(swipeActionHelper.getExposedMenuView());
}
- class LogMatcher implements ArgumentMatcher<LogMaker> {
- private int mCategory, mType;
-
- LogMatcher(int category, int type) {
- mCategory = category;
- mType = type;
- }
- public boolean matches(LogMaker l) {
- return (l.getCategory() == mCategory)
- && (l.getType() == mType);
- }
-
- public String toString() {
- return String.format("LogMaker(%d, %d)", mCategory, mType);
- }
- }
-
- private LogMaker logMatcher(int category, int type) {
- return argThat(new LogMatcher(category, type));
- }
-
- @Test
- @UiThreadTest
- public void testOnMenuClickedLogging() {
- // Set up the object under test to have a valid mLongPressListener. We're testing an
- // anonymous-class member, mMenuEventListener, so we need to modify the state of the
- // class itself, not the Mockito spy copied from it. See notes in setup.
- mStackScrollerInternal.setLongPressListener(
- mock(ExpandableNotificationRow.LongPressListener.class));
-
- ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
- when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
- MetricsProto.MetricsEvent.VIEW_UNKNOWN));
-
- mStackScroller.mMenuEventListener.onMenuClicked(row, 0, 0, mock(
- NotificationMenuRowPlugin.MenuItem.class));
- verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data
- verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR,
- MetricsProto.MetricsEvent.TYPE_ACTION));
- }
-
- @Test
- @UiThreadTest
- public void testOnMenuShownLogging() { ;
-
- ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
- when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
- MetricsProto.MetricsEvent.VIEW_UNKNOWN));
-
- mStackScroller.mMenuEventListener.onMenuShown(row);
- verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data
- verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR,
- MetricsProto.MetricsEvent.TYPE_ACTION));
- }
-
@Test
public void testClearNotifications_All() {
mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_ALL, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
index e3acf0213725..83d6ac904bfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
@@ -17,25 +17,45 @@
package com.android.systemui.statusbar.notification.stack;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.metrics.LogMaker;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.media.KeyguardMediaController;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -55,15 +75,27 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
@Mock
private TunerService mTunerService;
@Mock
- private AmbientState mAmbientState;
- @Mock
private DynamicPrivacyController mDynamicPrivacyController;
@Mock
private ConfigurationController mConfigurationController;
@Mock
private NotificationStackScrollLayout mNotificationStackScrollLayout;
+ @Mock
+ private ZenModeController mZenModeController;
+ @Mock
+ private KeyguardMediaController mKeyguardMediaController;
+ @Mock
+ private SysuiStatusBarStateController mSysuiStatusBarStateController;
+ @Mock
+ private KeyguardBypassController mKeyguardBypassController;
+ @Mock
+ private SysuiColorExtractor mColorExtractor;
+ @Mock
+ private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+ @Mock
+ private MetricsLogger mMetricsLogger;
- NotificationStackScrollLayoutController mController;
+ private NotificationStackScrollLayoutController mController;
@Before
public void setUp() {
@@ -76,7 +108,14 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
mNotificationRoundnessManager,
mTunerService,
mDynamicPrivacyController,
- mConfigurationController
+ mConfigurationController,
+ mSysuiStatusBarStateController,
+ mKeyguardMediaController,
+ mKeyguardBypassController,
+ mZenModeController,
+ mColorExtractor,
+ mNotificationLockscreenUserManager,
+ mMetricsLogger
);
when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
@@ -112,4 +151,140 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
mController.mConfigurationListener.onDensityOrFontScaleChanged();
verify(mNotificationStackScrollLayout).reinflateViews();
}
+
+ @Test
+ public void testUpdateEmptyShadeView_notificationsVisible() {
+ when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true);
+ mController.attach(mNotificationStackScrollLayout);
+
+ mController.updateEmptyShadeView(true /* visible */);
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ true /* visible */,
+ true /* notifVisibleInShade */);
+ reset(mNotificationStackScrollLayout);
+ mController.updateEmptyShadeView(false /* visible */);
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ false /* visible */,
+ true /* notifVisibleInShade */);
+ }
+
+ @Test
+ public void testUpdateEmptyShadeView_notificationsHidden() {
+ when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+ mController.attach(mNotificationStackScrollLayout);
+
+ mController.updateEmptyShadeView(true /* visible */);
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ true /* visible */,
+ false /* notifVisibleInShade */);
+ reset(mNotificationStackScrollLayout);
+ mController.updateEmptyShadeView(false /* visible */);
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ false /* visible */,
+ false /* notifVisibleInShade */);
+ }
+
+ @Test
+ public void testOnUserChange_verifySensitiveProfile() {
+ when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true);
+
+ ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor
+ .forClass(UserChangedListener.class);
+
+ mController.attach(mNotificationStackScrollLayout);
+ verify(mNotificationLockscreenUserManager)
+ .addUserChangedListener(userChangedCaptor.capture());
+ reset(mNotificationStackScrollLayout);
+
+ UserChangedListener changedListener = userChangedCaptor.getValue();
+ changedListener.onUserChanged(0);
+ verify(mNotificationStackScrollLayout).setCurrentUserid(0);
+ verify(mNotificationStackScrollLayout).updateSensitiveness(false, true);
+ }
+
+ @Test
+ public void testOnStatePostChange_verifyIfProfileIsPublic() {
+ when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true);
+
+ ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
+ ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
+
+ mController.attach(mNotificationStackScrollLayout);
+ verify(mSysuiStatusBarStateController).addCallback(
+ stateListenerArgumentCaptor.capture(), anyInt());
+
+ StatusBarStateController.StateListener stateListener =
+ stateListenerArgumentCaptor.getValue();
+
+ stateListener.onStatePostChange();
+ verify(mNotificationStackScrollLayout).updateSensitiveness(false, true);
+ }
+
+
+ @Test
+ public void testOnMenuShownLogging() { ;
+
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
+ when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
+ MetricsProto.MetricsEvent.VIEW_UNKNOWN));
+
+
+ ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor =
+ ArgumentCaptor.forClass(OnMenuEventListener.class);
+
+ mController.attach(mNotificationStackScrollLayout);
+ verify(mNotificationStackScrollLayout).setMenuEventListener(
+ onMenuEventListenerArgumentCaptor.capture());
+
+ OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue();
+
+ onMenuEventListener.onMenuShown(row);
+ verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data
+ verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR,
+ MetricsProto.MetricsEvent.TYPE_ACTION));
+ }
+
+ @Test
+ public void testOnMenuClickedLogging() {
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
+ when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
+ MetricsProto.MetricsEvent.VIEW_UNKNOWN));
+
+
+ ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor =
+ ArgumentCaptor.forClass(OnMenuEventListener.class);
+
+ mController.attach(mNotificationStackScrollLayout);
+ verify(mNotificationStackScrollLayout).setMenuEventListener(
+ onMenuEventListenerArgumentCaptor.capture());
+
+ OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue();
+
+ onMenuEventListener.onMenuClicked(row, 0, 0, mock(
+ NotificationMenuRowPlugin.MenuItem.class));
+ verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data
+ verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR,
+ MetricsProto.MetricsEvent.TYPE_ACTION));
+ }
+
+ private LogMaker logMatcher(int category, int type) {
+ return argThat(new LogMatcher(category, type));
+ }
+
+ static class LogMatcher implements ArgumentMatcher<LogMaker> {
+ private int mCategory, mType;
+
+ LogMatcher(int category, int type) {
+ mCategory = category;
+ mType = type;
+ }
+ public boolean matches(LogMaker l) {
+ return (l.getCategory() == mCategory)
+ && (l.getType() == mType);
+ }
+
+ public String toString() {
+ return String.format("LogMaker(%d, %d)", mCategory, mType);
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index ccc307841491..2f4511329041 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -43,6 +43,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.FalsingManager;
@@ -58,6 +59,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -89,6 +92,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
private View mNotificationContainer;
@Mock
private KeyguardBypassController mBypassController;
+ @Mock
+ private FaceAuthScreenBrightnessController mFaceAuthScreenBrightnessController;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Before
@@ -108,6 +113,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mock(DockManager.class),
mock(NotificationShadeWindowController.class),
mKeyguardStateController,
+ mFaceAuthScreenBrightnessController,
mock(NotificationMediaManager.class));
mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
mNotificationPanelView, mBiometrucUnlockController, mDismissCallbackRegistry,
@@ -274,11 +280,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
DockManager dockManager,
NotificationShadeWindowController notificationShadeWindowController,
KeyguardStateController keyguardStateController,
+ FaceAuthScreenBrightnessController faceAuthScreenBrightnessController,
NotificationMediaManager notificationMediaManager) {
super(context, callback, lockPatternUtils, sysuiStatusBarStateController,
configurationController, keyguardUpdateMonitor, navigationModeController,
dockManager, notificationShadeWindowController, keyguardStateController,
- notificationMediaManager);
+ Optional.of(faceAuthScreenBrightnessController), notificationMediaManager);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java
index 260ff2dafeed..33ece0084906 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java
@@ -70,9 +70,7 @@ public class DeviceConfigProxyFake extends DeviceConfigProxy {
for (Pair<Executor, OnPropertiesChangedListener> listener : mListeners) {
Properties.Builder propBuilder = new Properties.Builder(namespace);
- for (String key : mProperties.get(namespace).keySet()) {
- propBuilder.setString(key, mProperties.get(namespace).get(key));
- }
+ propBuilder.setString(name, value);
listener.first.execute(() -> listener.second.onPropertiesChanged(propBuilder.build()));
}
return true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java
new file mode 100644
index 000000000000..64e89579393e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import static android.provider.DeviceConfig.Properties;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.provider.DeviceConfig.OnPropertiesChangedListener;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DeviceConfigProxyFakeTest extends SysuiTestCase {
+ private static final String NAMESPACE = "foobar";
+
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+ private DeviceConfigProxyFake mDeviceConfigProxyFake;
+
+ @Before
+ public void setup() {
+ mDeviceConfigProxyFake = new DeviceConfigProxyFake();
+ }
+
+ @Test
+ public void testOnPropertiesChanged() {
+ TestableListener onPropertiesChangedListener = new TestableListener();
+ String key = "foo";
+ String value = "bar";
+
+ mDeviceConfigProxyFake.addOnPropertiesChangedListener(
+ NAMESPACE, mFakeExecutor, onPropertiesChangedListener);
+
+ mDeviceConfigProxyFake.setProperty(NAMESPACE, key, value, false);
+ mFakeExecutor.runAllReady();
+ assertThat(onPropertiesChangedListener.mProperties).isNotNull();
+ assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1);
+ assertThat(onPropertiesChangedListener.mProperties.getString(key, "")).isEqualTo(value);
+ }
+
+ @Test
+ public void testOnMultiplePropertiesChanged() {
+ TestableListener onPropertiesChangedListener = new TestableListener();
+ String keyA = "foo";
+ String valueA = "bar";
+ String keyB = "bada";
+ String valueB = "boom";
+
+ mDeviceConfigProxyFake.addOnPropertiesChangedListener(
+ NAMESPACE, mFakeExecutor, onPropertiesChangedListener);
+ mDeviceConfigProxyFake.setProperty(NAMESPACE, keyA, valueA, false);
+ mFakeExecutor.runAllReady();
+ assertThat(onPropertiesChangedListener.mProperties).isNotNull();
+ assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1);
+ assertThat(onPropertiesChangedListener.mProperties.getString(keyA, "")).isEqualTo(valueA);
+
+ mDeviceConfigProxyFake.setProperty(NAMESPACE, keyB, valueB, false);
+ mFakeExecutor.runAllReady();
+ assertThat(onPropertiesChangedListener.mProperties).isNotNull();
+ assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1);
+ assertThat(onPropertiesChangedListener.mProperties.getString(keyB, "")).isEqualTo(valueB);
+ }
+
+ private static class TestableListener implements OnPropertiesChangedListener {
+ Properties mProperties;
+
+ TestableListener() {
+ }
+ @Override
+ public void onPropertiesChanged(@NonNull Properties properties) {
+ mProperties = properties;
+ }
+ }
+}
diff --git a/services/Android.bp b/services/Android.bp
index b348b91a1bd7..ef52c2aff002 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -156,10 +156,14 @@ droidstubs {
java_library {
name: "android_system_server_stubs_current",
+ defaults: ["android_stubs_dists_default"],
srcs: [":services-stubs.sources"],
installable: false,
static_libs: ["android_module_lib_stubs_current"],
sdk_version: "none",
system_modules: "none",
java_version: "1.8",
+ dist: {
+ dir: "apistubs/android/system-server",
+ },
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 669bb24e0e77..a75b64ce08f5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -284,11 +284,13 @@ public class AccessibilityWindowManager {
* Computes partial interactive region of given windowId.
*
* @param windowId The windowId
+ * @param forceComputeRegion set outRegion when the windowId matches one on the screen even
+ * though the region is not covered by other windows above it.
* @param outRegion The output to which to write the bounds.
- * @return true if outRegion is not empty.
+ * @return {@code true} if outRegion is not empty.
*/
boolean computePartialInteractiveRegionForWindowLocked(int windowId,
- @NonNull Region outRegion) {
+ boolean forceComputeRegion, @NonNull Region outRegion) {
if (mWindows == null) {
return false;
}
@@ -309,6 +311,9 @@ public class AccessibilityWindowManager {
currentWindow.getRegionInScreen(currentWindowRegions);
outRegion.set(currentWindowRegions);
windowInteractiveRegion = outRegion;
+ if (forceComputeRegion) {
+ windowInteractiveRegionChanged = true;
+ }
continue;
}
} else if (currentWindow.getType()
@@ -1240,10 +1245,13 @@ public class AccessibilityWindowManager {
*/
public boolean computePartialInteractiveRegionForWindowLocked(int windowId,
@NonNull Region outRegion) {
- windowId = resolveParentWindowIdLocked(windowId);
- final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
+ final int parentWindowId = resolveParentWindowIdLocked(windowId);
+ final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(
+ parentWindowId);
+
if (observer != null) {
- return observer.computePartialInteractiveRegionForWindowLocked(windowId, outRegion);
+ return observer.computePartialInteractiveRegionForWindowLocked(parentWindowId,
+ parentWindowId != windowId, outRegion);
}
return false;
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index a2d58c8019fc..b587dd33c4e5 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -561,24 +561,6 @@ public class TouchExplorer extends BaseEventStreamTransformation
// stream consistent.
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
}
- if (mGestureDetector.isMultiFingerGesturesEnabled()
- && mGestureDetector.isTwoFingerPassthroughEnabled()) {
- if (event.getPointerCount() == 3) {
- boolean isOnBottomEdge = false;
- // If three fingers go down on the bottom edge of the screen, delegate immediately.
- final long screenHeight = mContext.getResources().getDisplayMetrics().heightPixels;
- for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) {
- if (mReceivedPointerTracker.getReceivedPointerDownY(i)
- > (screenHeight - mEdgeSwipeHeightPixels)) {
- isOnBottomEdge = true;
- }
- }
- if (isOnBottomEdge) {
- mState.startDelegating();
- mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
- }
- }
- }
}
/**
@@ -644,12 +626,34 @@ public class TouchExplorer extends BaseEventStreamTransformation
break;
default:
if (mGestureDetector.isMultiFingerGesturesEnabled()) {
- return;
+ if (mGestureDetector.isTwoFingerPassthroughEnabled()) {
+ if (event.getPointerCount() == 3) {
+ boolean isOnBottomEdge = true;
+ // If three fingers went down on the bottom edge of the screen, delegate
+ // immediately.
+ final long screenHeight =
+ mContext.getResources().getDisplayMetrics().heightPixels;
+ for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) {
+ if (mReceivedPointerTracker.getReceivedPointerDownY(i)
+ < (screenHeight - mEdgeSwipeHeightPixels)) {
+ isOnBottomEdge = false;
+ }
+ }
+ if (isOnBottomEdge) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Three-finger edge swipe detected.");
+ }
+ mState.startDelegating();
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
+ }
+ }
+ }
+ } else {
+ // More than two pointers are delegated to the view hierarchy.
+ mState.startDelegating();
+ event = MotionEvent.obtainNoHistory(event);
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
}
- // More than two pointers are delegated to the view hierarchy.
- mState.startDelegating();
- event = MotionEvent.obtainNoHistory(event);
- mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
break;
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 6d71c8e68f77..19871f993d42 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -315,11 +315,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>>
mPreciseDataConnectionStates;
- static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK =
- PhoneStateListener.LISTEN_REGISTRATION_FAILURE
- | PhoneStateListener.LISTEN_BARRING_INFO;
-
- static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK =
+ // Starting in Q, almost all cellular location requires FINE location enforcement.
+ // Prior to Q, cellular was available with COARSE location enforcement. Bits in this
+ // list will be checked for COARSE on apps targeting P or earlier and FINE on Q or later.
+ static final int ENFORCE_LOCATION_PERMISSION_MASK =
PhoneStateListener.LISTEN_CELL_LOCATION
| PhoneStateListener.LISTEN_CELL_INFO
| PhoneStateListener.LISTEN_REGISTRATION_FAILURE
@@ -376,7 +375,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ " newDefaultPhoneId=" + newDefaultPhoneId);
}
- //Due to possible risk condition,(notify call back using the new
+ //Due to possible race condition,(notify call back using the new
//defaultSubId comes before new defaultSubId update) we need to recall all
//possible missed notify callback
synchronized (mRecords) {
@@ -909,7 +908,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) {
try {
if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]);
- if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+ && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
// null will be translated to empty CellLocation object in client.
r.callback.onCellLocationChanged(mCellIdentity[phoneId]);
}
@@ -964,7 +964,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
try {
if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
- if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+ && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
}
} catch (RemoteException ex) {
@@ -1518,7 +1519,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
idMatch(r.subId, subId, phoneId) &&
- checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+ && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
try {
if (DBG_LOC) {
log("notifyCellInfoForSubscriber: mCellInfo=" + cellInfo
@@ -1797,7 +1799,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
idMatch(r.subId, subId, phoneId) &&
- checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+ && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
try {
if (DBG_LOC) {
log("notifyCellLocation: cellLocation=" + cellLocation
@@ -2544,16 +2547,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
boolean shouldCheckLocationPermissions = false;
- if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) {
+ if ((events & ENFORCE_LOCATION_PERMISSION_MASK) != 0) {
// Everything that requires fine location started in Q. So far...
locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q);
- // If we're enforcing fine starting in Q, we also want to enforce coarse starting in Q.
- locationQueryBuilder.setMinSdkVersionForCoarse(Build.VERSION_CODES.Q);
- locationQueryBuilder.setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q);
- shouldCheckLocationPermissions = true;
- }
-
- if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) {
+ // If we're enforcing fine starting in Q, we also want to enforce coarse even for
+ // older SDK versions.
locationQueryBuilder.setMinSdkVersionForCoarse(0);
locationQueryBuilder.setMinSdkVersionForEnforcement(0);
shouldCheckLocationPermissions = true;
@@ -2750,8 +2748,16 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
try {
if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" +
mServiceState[phoneId]);
- r.callback.onServiceStateChanged(
- new ServiceState(mServiceState[phoneId]));
+ ServiceState ss = new ServiceState(mServiceState[phoneId]);
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ r.callback.onServiceStateChanged(ss);
+ } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+ r.callback.onServiceStateChanged(
+ ss.createLocationInfoSanitizedCopy(false));
+ } else {
+ r.callback.onServiceStateChanged(
+ ss.createLocationInfoSanitizedCopy(true));
+ }
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -2796,7 +2802,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
}
- if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+ && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
}
} catch (RemoteException ex) {
@@ -2862,7 +2869,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = "
+ mCellIdentity[phoneId]);
}
- if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+ && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
// null will be translated to empty CellLocation object in client.
r.callback.onCellLocationChanged(mCellIdentity[phoneId]);
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 1815dac4c3a8..990a547144a0 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -99,6 +99,7 @@ public class Watchdog {
"android.hardware.audio@4.0::IDevicesFactory",
"android.hardware.audio@5.0::IDevicesFactory",
"android.hardware.audio@6.0::IDevicesFactory",
+ "android.hardware.audio@7.0::IDevicesFactory",
"android.hardware.biometrics.face@1.0::IBiometricsFace",
"android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint",
"android.hardware.bluetooth@1.0::IBluetoothHci",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6328cb60cf73..c31d73246ff6 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -453,16 +453,16 @@ public final class ActiveServices {
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
- int callingPid, int callingUid, boolean fgRequired, String callingPackage,
- @Nullable String callingFeatureId, final int userId)
+ int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification,
+ String callingPackage, @Nullable String callingFeatureId, final int userId)
throws TransactionTooLargeException {
return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
- callingPackage, callingFeatureId, userId, false);
+ hideFgNotification, callingPackage, callingFeatureId, userId, false);
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
- int callingPid, int callingUid, boolean fgRequired, String callingPackage,
- @Nullable String callingFeatureId, final int userId,
+ int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification,
+ String callingPackage, @Nullable String callingFeatureId, final int userId,
boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
@@ -626,6 +626,7 @@ public final class ActiveServices {
r.startRequested = true;
r.delayedStop = false;
r.fgRequired = fgRequired;
+ r.hideFgNotification = hideFgNotification;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants, callingUid));
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index e9539be8b0d5..fede1d2832b8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -89,6 +89,7 @@ final class ActivityManagerConstants extends ContentObserver {
static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time";
static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration";
static final String KEY_PENDINGINTENT_WARNING_THRESHOLD = "pendingintent_warning_threshold";
+ static final String KEY_MIN_CRASH_INTERVAL = "min_crash_interval";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -122,6 +123,8 @@ final class ActivityManagerConstants extends ContentObserver {
private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000;
private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000;
private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000;
+ private static final int DEFAULT_MIN_CRASH_INTERVAL = 2 * 60 * 1000;
+
// Flag stored in the DeviceConfig API.
/**
@@ -281,6 +284,12 @@ final class ActivityManagerConstants extends ContentObserver {
// this long.
public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION;
+ /**
+ * The minimum time we allow between crashes, for us to consider this
+ * application to be bad and stop its services and reject broadcasts.
+ */
+ public static int MIN_CRASH_INTERVAL = DEFAULT_MIN_CRASH_INTERVAL;
+
// Indicates whether the activity starts logging is enabled.
// Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
volatile boolean mFlagActivityStartsLoggingEnabled;
@@ -650,6 +659,8 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_MEMORY_INFO_THROTTLE_TIME);
TOP_TO_FGS_GRACE_DURATION = mParser.getDurationMillis(KEY_TOP_TO_FGS_GRACE_DURATION,
DEFAULT_TOP_TO_FGS_GRACE_DURATION);
+ MIN_CRASH_INTERVAL = mParser.getInt(KEY_MIN_CRASH_INTERVAL,
+ DEFAULT_MIN_CRASH_INTERVAL);
PENDINGINTENT_WARNING_THRESHOLD = mParser.getInt(KEY_PENDINGINTENT_WARNING_THRESHOLD,
DEFAULT_PENDINGINTENT_WARNING_THRESHOLD);
@@ -866,6 +877,8 @@ final class ActivityManagerConstants extends ContentObserver {
pw.println(MEMORY_INFO_THROTTLE_TIME);
pw.print(" "); pw.print(KEY_TOP_TO_FGS_GRACE_DURATION); pw.print("=");
pw.println(TOP_TO_FGS_GRACE_DURATION);
+ pw.print(" "); pw.print(KEY_MIN_CRASH_INTERVAL); pw.print("=");
+ pw.println(MIN_CRASH_INTERVAL);
pw.print(" "); pw.print(KEY_IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES); pw.print("=");
pw.println(Arrays.toString(IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.toArray()));
pw.print(" "); pw.print(KEY_IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES); pw.print("=");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3e600b793385..6e1e3d0a9a9a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1370,6 +1370,10 @@ public class ActivityManagerService extends IActivityManager.Stub
final Injector mInjector;
+ /** The package verifier app. */
+ private String mPackageVerifier;
+ private int mPackageVerifierUid = UserHandle.USER_NULL;
+
static final class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
static final int CHANGE_FOREGROUND_SERVICES = 1<<1;
@@ -2246,6 +2250,18 @@ public class ActivityManagerService extends IActivityManager.Stub
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mService.mBatteryStatsService.systemServicesReady();
mService.mServices.systemServicesReady();
+ mService.mPackageVerifier = ArrayUtils.firstOrNull(
+ LocalServices.getService(PackageManagerInternal.class).getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM));
+ if (mService.mPackageVerifier != null) {
+ try {
+ mService.mPackageVerifierUid =
+ getContext().getPackageManager().getPackageUid(
+ mService.mPackageVerifier, UserHandle.USER_SYSTEM);
+ } catch (NameNotFoundException e) {
+ Slog.wtf(TAG, "Package manager couldn't get package verifier uid", e);
+ }
+ }
} else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mService.startBroadcastObservers();
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
@@ -13263,8 +13279,8 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
- String resolvedType, boolean requireForeground, String callingPackage,
- String callingFeatureId, int userId)
+ String resolvedType, boolean requireForeground, boolean hideForegroundNotification,
+ String callingPackage, String callingFeatureId, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
@@ -13276,17 +13292,27 @@ public class ActivityManagerService extends IActivityManager.Stub
throw new IllegalArgumentException("callingPackage cannot be null");
}
+ final int callingUid = Binder.getCallingUid();
+ if (requireForeground && hideForegroundNotification) {
+ if (!UserHandle.isSameApp(callingUid, mPackageVerifierUid)
+ || !callingPackage.equals(mPackageVerifier)) {
+ throw new IllegalArgumentException(
+ "Only the package verifier can hide its foreground service notification");
+ }
+ Slog.i(TAG, "Foreground service notification hiding requested by " + callingPackage);
+ }
+
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
synchronized(this) {
final int callingPid = Binder.getCallingPid();
- final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res;
try {
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
- requireForeground, callingPackage, callingFeatureId, userId);
+ requireForeground, hideForegroundNotification,
+ callingPackage, callingFeatureId, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -17670,7 +17696,7 @@ public class ActivityManagerService extends IActivityManager.Stub
ComponentName res;
try {
res = mServices.startServiceLocked(null, service,
- resolvedType, -1, uid, fgRequired, callingPackage,
+ resolvedType, -1, uid, fgRequired, false, callingPackage,
callingFeatureId, userId, allowBackgroundActivityStarts);
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 149e3baa90e7..a512cca7bac4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -654,7 +654,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println("Starting service: " + intent);
pw.flush();
ComponentName cn = mInterface.startService(null, intent, intent.getType(),
- asForeground, SHELL_PACKAGE_NAME, null, mUserId);
+ asForeground, false, SHELL_PACKAGE_NAME, null, mUserId);
if (cn == null) {
err.println("Error: Not found; no service started.");
return -1;
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 2e92ac0fb3d6..5268359df327 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -716,7 +716,7 @@ class AppErrors {
// back in the pending list.
ServiceRecord sr = app.getRunningServiceAt(i);
// If the service was restarted a while ago, then reset crash count, else increment it.
- if (now > sr.restartTime + ProcessList.MIN_CRASH_INTERVAL) {
+ if (now > sr.restartTime + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
sr.crashCount = 1;
} else {
sr.crashCount++;
@@ -729,7 +729,7 @@ class AppErrors {
}
}
- if (crashTime != null && now < crashTime + ProcessList.MIN_CRASH_INTERVAL) {
+ if (crashTime != null && now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
// The process crashed again very quickly. If it was a bound foreground service, let's
// try to restart again in a while, otherwise the process loses!
Slog.w(TAG, "Process " + app.processName
@@ -771,7 +771,7 @@ class AppErrors {
data.taskId = affectedTaskId;
}
if (data != null && crashTimePersistent != null
- && now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) {
+ && now < crashTimePersistent + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
data.repeating = true;
}
}
@@ -853,7 +853,7 @@ class AppErrors {
mAppsNotReportingCrashes.contains(proc.info.packageName);
final long now = SystemClock.uptimeMillis();
final boolean shouldThottle = crashShowErrorTime != null
- && now < crashShowErrorTime + ProcessList.MIN_CRASH_INTERVAL;
+ && now < crashShowErrorTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
&& !crashSilenced && !shouldThottle
&& (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4673ecde7a68..76089f8fba01 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -154,10 +154,6 @@ public final class ProcessList {
static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.sys.vold_app_data_isolation_enabled";
- // 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;
-
// OOM adjustments for processes in various states:
// Uninitialized value for any major or minor adj fields
@@ -4070,7 +4066,8 @@ public final class ProcessList {
boolean enqueueLocked(ProcessRecord app, String reason, int requester) {
// Throttle the killing request for potential bad app to avoid cpu thrashing
Long last = app.isolated ? null : mLastProcessKillTimes.get(app.processName, app.uid);
- if (last != null && SystemClock.uptimeMillis() < last + MIN_CRASH_INTERVAL) {
+ if ((last != null) && (SystemClock.uptimeMillis()
+ < (last + ActivityManagerConstants.MIN_CRASH_INTERVAL))) {
return false;
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 66677b67b6aa..5b12c8ce6582 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -104,6 +104,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
boolean delayed; // are we waiting to start this service in the background?
boolean fgRequired; // is the service required to go foreground after starting?
+ boolean hideFgNotification; // Hide the fg service notification
boolean fgWaiting; // is a timeout for going foreground already scheduled?
boolean isForeground; // is service currently in foreground mode?
int foregroundId; // Notification ID of last foreground req.
@@ -836,6 +837,9 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
}
public void postNotification() {
+ if (hideFgNotification) {
+ return;
+ }
final int appUid = appInfo.uid;
final int appPid = app.pid;
if (foregroundId != 0 && foregroundNoti != null) {
@@ -928,7 +932,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
}
if (localForegroundNoti.getSmallIcon() == null) {
// Notifications whose icon is 0 are defined to not show
- // a notification, silently ignoring it. We don't want to
+ // a notification. We don't want to
// just ignore it, we want to prevent the service from
// being foreground.
throw new RuntimeException("invalid service notification: "
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0a179e89f757..673ca1f3da86 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -285,6 +285,8 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29;
private static final int MSG_BROADCAST_MICROPHONE_MUTE = 30;
private static final int MSG_CHECK_MODE_FOR_UID = 31;
+ private static final int MSG_STREAM_DEVICES_CHANGED = 32;
+ private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -1283,7 +1285,6 @@ public class AudioService extends IAudioService.Stub
}
if (isPlatformTelevision()) {
- checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, caller);
synchronized (mHdmiClientLock) {
if (mHdmiManager != null && mHdmiPlaybackClient != null) {
updateHdmiCecSinkLocked(mHdmiCecSink | false);
@@ -1303,22 +1304,71 @@ public class AudioService extends IAudioService.Stub
}
}
- private void checkAddAllFixedVolumeDevices(int device, String caller) {
+ /**
+ * Asynchronously update volume states for the given device.
+ *
+ * @param device a single audio device, ensure that this is not a devices bitmask
+ * @param caller caller of this method
+ */
+ private void postUpdateVolumeStatesForAudioDevice(int device, String caller) {
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_VOLUME_STATES_FOR_DEVICE,
+ SENDMSG_QUEUE, device /*arg1*/, 0 /*arg2*/, caller /*obj*/,
+ 0 /*delay*/);
+ }
+
+ /**
+ * Update volume states for the given device.
+ *
+ * This will initialize the volume index if no volume index is available.
+ * If the device is the currently routed device, fixed/full volume policies will be applied.
+ *
+ * @param device a single audio device, ensure that this is not a devices bitmask
+ * @param caller caller of this method
+ */
+ private void onUpdateVolumeStatesForAudioDevice(int device, String caller) {
final int numStreamTypes = AudioSystem.getNumStreamTypes();
- for (int streamType = 0; streamType < numStreamTypes; streamType++) {
- if (!mStreamStates[streamType].hasIndexForDevice(device)) {
- // set the default value, if device is affected by a full/fix/abs volume rule, it
- // will taken into account in checkFixedVolumeDevices()
- mStreamStates[streamType].setIndex(
- mStreamStates[mStreamVolumeAlias[streamType]]
- .getIndex(AudioSystem.DEVICE_OUT_DEFAULT),
- device, caller, true /*hasModifyAudioSettings*/);
+ synchronized (mSettingsLock) {
+ synchronized (VolumeStreamState.class) {
+ for (int streamType = 0; streamType < numStreamTypes; streamType++) {
+ updateVolumeStates(device, streamType, caller);
+ }
}
- mStreamStates[streamType].checkFixedVolumeDevices();
+ }
+ }
- // Unmute streams if device is full volume
- if (mFullVolumeDevices.contains(device)) {
- mStreamStates[streamType].mute(false);
+ /**
+ * Update volume states for the given device and given stream.
+ *
+ * This will initialize the volume index if no volume index is available.
+ * If the device is the currently routed device, fixed/full volume policies will be applied.
+ *
+ * @param device a single audio device, ensure that this is not a devices bitmask
+ * @param streamType streamType to be updated
+ * @param caller caller of this method
+ */
+ private void updateVolumeStates(int device, int streamType, String caller) {
+ if (!mStreamStates[streamType].hasIndexForDevice(device)) {
+ // set the default value, if device is affected by a full/fix/abs volume rule, it
+ // will taken into account in checkFixedVolumeDevices()
+ mStreamStates[streamType].setIndex(
+ mStreamStates[mStreamVolumeAlias[streamType]]
+ .getIndex(AudioSystem.DEVICE_OUT_DEFAULT),
+ device, caller, true /*hasModifyAudioSettings*/);
+ }
+
+ // Check if device to be updated is routed for the given audio stream
+ List<AudioDeviceAttributes> devicesForAttributes = getDevicesForAttributesInt(
+ new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build());
+ for (AudioDeviceAttributes deviceAttributes : devicesForAttributes) {
+ if (deviceAttributes.getType() == AudioDeviceInfo.convertInternalDeviceToDeviceType(
+ device)) {
+ mStreamStates[streamType].checkFixedVolumeDevices();
+
+ // Unmute streams if required and device is full volume
+ if (isStreamMute(streamType) && mFullVolumeDevices.contains(device)) {
+ mStreamStates[streamType].mute(false);
+ }
}
}
}
@@ -1868,8 +1918,13 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
- Objects.requireNonNull(attributes);
enforceModifyAudioRoutingPermission();
+ return getDevicesForAttributesInt(attributes);
+ }
+
+ protected @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesInt(
+ @NonNull AudioAttributes attributes) {
+ Objects.requireNonNull(attributes);
return AudioSystem.getDevicesForAttributes(attributes);
}
@@ -4912,11 +4967,21 @@ public class AudioService extends IAudioService.Stub
}
}
- private void observeDevicesForStreams(int skipStream) {
- synchronized (VolumeStreamState.class) {
- for (int stream = 0; stream < mStreamStates.length; stream++) {
- if (stream != skipStream) {
- mStreamStates[stream].observeDevicesForStream_syncVSS(false /*checkOthers*/);
+ private void onObserveDevicesForAllStreams(int skipStream) {
+ synchronized (mSettingsLock) {
+ synchronized (VolumeStreamState.class) {
+ for (int stream = 0; stream < mStreamStates.length; stream++) {
+ if (stream != skipStream) {
+ int devices = mStreamStates[stream].observeDevicesForStream_syncVSS(
+ false /*checkOthers*/);
+
+ Set<Integer> devicesSet = AudioSystem.generateAudioDeviceTypesSet(devices);
+ for (Integer device : devicesSet) {
+ // Update volume states for devices routed for the stream
+ updateVolumeStates(device, stream,
+ "AudioService#onObserveDevicesForAllStreams");
+ }
+ }
}
}
}
@@ -4925,16 +4990,18 @@ public class AudioService extends IAudioService.Stub
/** only public for mocking/spying, do not call outside of AudioService */
@VisibleForTesting
public void postObserveDevicesForAllStreams() {
+ postObserveDevicesForAllStreams(-1);
+ }
+
+ /** only public for mocking/spying, do not call outside of AudioService */
+ @VisibleForTesting
+ public void postObserveDevicesForAllStreams(int skipStream) {
sendMsg(mAudioHandler,
MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS,
- SENDMSG_QUEUE, 0 /*arg1*/, 0 /*arg2*/, null /*obj*/,
+ SENDMSG_QUEUE, skipStream /*arg1*/, 0 /*arg2*/, null /*obj*/,
0 /*delay*/);
}
- private void onObserveDevicesForAllStreams() {
- observeDevicesForStreams(-1);
- }
-
/**
* @see AudioManager#setDeviceVolumeBehavior(AudioDeviceAttributes, int)
* @param device the audio device to be affected
@@ -4985,7 +5052,8 @@ public class AudioService extends IAudioService.Stub
+ Integer.toHexString(audioSystemDeviceOut) + " from:" + caller));
// make sure we have a volume entry for this device, and that volume is updated according
// to volume behavior
- checkAddAllFixedVolumeDevices(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller);
+ postUpdateVolumeStatesForAudioDevice(audioSystemDeviceOut,
+ "setDeviceVolumeBehavior:" + caller);
}
/**
@@ -5616,6 +5684,7 @@ public class AudioService extends IAudioService.Stub
}
}
+ @GuardedBy("VolumeStreamState.class")
public int observeDevicesForStream_syncVSS(boolean checkOthers) {
if (!mSystemServer.isPrivileged()) {
return AudioSystem.DEVICE_NONE;
@@ -5628,15 +5697,19 @@ public class AudioService extends IAudioService.Stub
mObservedDevices = devices;
if (checkOthers) {
// one stream's devices have changed, check the others
- observeDevicesForStreams(mStreamType);
+ postObserveDevicesForAllStreams(mStreamType);
}
// log base stream changes to the event log
if (mStreamVolumeAlias[mStreamType] == mStreamType) {
EventLogTags.writeStreamDevicesChanged(mStreamType, prevDevices, devices);
}
- sendBroadcastToAll(mStreamDevicesChanged
- .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, prevDevices)
- .putExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, devices));
+ // send STREAM_DEVICES_CHANGED_ACTION on the message handler so it is scheduled after
+ // the postObserveDevicesForStreams is handled
+ sendMsg(mAudioHandler,
+ MSG_STREAM_DEVICES_CHANGED,
+ SENDMSG_QUEUE, prevDevices /*arg1*/, devices /*arg2*/,
+ // ok to send reference to this object, it is final
+ mStreamDevicesChanged /*obj*/, 0 /*delay*/);
return devices;
}
@@ -6408,7 +6481,7 @@ public class AudioService extends IAudioService.Stub
break;
case MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS:
- onObserveDevicesForAllStreams();
+ onObserveDevicesForAllStreams(/*skipStream*/ msg.arg1);
break;
case MSG_HDMI_VOLUME_CHECK:
@@ -6451,6 +6524,16 @@ public class AudioService extends IAudioService.Stub
mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid()));
}
break;
+
+ case MSG_STREAM_DEVICES_CHANGED:
+ sendBroadcastToAll(((Intent) msg.obj)
+ .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, msg.arg1)
+ .putExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, msg.arg2));
+ break;
+
+ case MSG_UPDATE_VOLUME_STATES_FOR_DEVICE:
+ onUpdateVolumeStatesForAudioDevice(msg.arg1, (String) msg.obj);
+ break;
}
}
}
@@ -7207,10 +7290,9 @@ public class AudioService extends IAudioService.Stub
// HDMI output
removeAudioSystemDeviceOutFromFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI);
}
+ postUpdateVolumeStatesForAudioDevice(AudioSystem.DEVICE_OUT_HDMI,
+ "HdmiPlaybackClient.DisplayStatusCallback");
}
-
- checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI,
- "HdmiPlaybackClient.DisplayStatusCallback");
}
private class MyHdmiControlStatusChangeListenerCallback
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 7f9b3c9fcff7..a75a80a606eb 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -21,23 +21,14 @@ import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.INTERNET;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
-import static android.net.INetd.PERMISSION_INTERNET;
-import static android.net.INetd.PERMISSION_NETWORK;
-import static android.net.INetd.PERMISSION_NONE;
-import static android.net.INetd.PERMISSION_SYSTEM;
-import static android.net.INetd.PERMISSION_UNINSTALLED;
-import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
-import static com.android.internal.util.ArrayUtils.convertToIntArray;
-
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -56,9 +47,11 @@ import android.system.OsConstants;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
@@ -72,6 +65,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+
/**
* A utility class to inform Netd of UID permisisons.
* Does a mass update at boot and then monitors for app install/remove.
@@ -120,51 +114,9 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
public int getDeviceFirstSdkInt() {
return Build.VERSION.FIRST_SDK_INT;
}
-
- /**
- * Check whether given uid has specific permission.
- */
- public int uidPermission(@NonNull final String permission, final int uid) {
- return ActivityManager.checkUidPermission(permission, uid);
- }
}
- /**
- * A data class to store each uid Netd permission information. Netd permissions includes
- * PERMISSION_NETWORK, PERMISSION_SYSTEM, PERMISSION_INTERNET, PERMISSION_UPDATE_DEVICE_STATS
- * and OR'd with the others. Default permission is PERMISSION_NONE and PERMISSION_UNINSTALLED
- * will be set if all packages are removed from the uid.
- */
- public static class UidNetdPermissionInfo {
- private final int mNetdPermissions;
-
- UidNetdPermissionInfo() {
- this(PERMISSION_NONE);
- }
-
- UidNetdPermissionInfo(int permissions) {
- mNetdPermissions = permissions;
- }
-
- /** Plus given permissions and return new UidNetdPermissionInfo instance. */
- public UidNetdPermissionInfo plusNetdPermissions(int permissions) {
- return new UidNetdPermissionInfo(mNetdPermissions | permissions);
- }
-
- /** Return whether package is uninstalled. */
- public boolean isPackageUninstalled() {
- return mNetdPermissions == PERMISSION_UNINSTALLED;
- }
-
- /** Check that uid has given permissions */
- public boolean hasNetdPermissions(final int permissions) {
- if (isPackageUninstalled()) return false;
- if (permissions == PERMISSION_NONE) return true;
- return (mNetdPermissions & permissions) == permissions;
- }
- }
-
- public PermissionMonitor(Context context, INetd netd) {
+ public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) {
this(context, netd, new Dependencies());
}
@@ -195,7 +147,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
return;
}
- final SparseArray<UidNetdPermissionInfo> netdPermsUids = new SparseArray<>();
+ SparseIntArray netdPermsUids = new SparseIntArray();
for (PackageInfo app : apps) {
int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
@@ -204,9 +156,8 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
}
mAllApps.add(UserHandle.getAppId(uid));
- final boolean isNetwork = hasPermission(CHANGE_NETWORK_STATE, uid);
- final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(uid)
- || isCarryoverPackage(app.applicationInfo);
+ boolean isNetwork = hasNetworkPermission(app);
+ boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
if (isNetwork || hasRestrictedPermission) {
Boolean permission = mApps.get(uid);
@@ -217,13 +168,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
}
}
- // Skip already checked uid.
- if (netdPermsUids.get(uid) != null) continue;
-
//TODO: unify the management of the permissions into one codepath.
- final UidNetdPermissionInfo permInfo =
- new UidNetdPermissionInfo(getNetdPermissionMask(uid));
- netdPermsUids.put(uid, permInfo);
+ int otherNetdPerms = getNetdPermissionMask(app.requestedPermissions,
+ app.requestedPermissionsFlags);
+ netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms);
}
List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users
@@ -238,17 +186,15 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
for (int i = 0; i < systemPermission.size(); i++) {
ArraySet<String> perms = systemPermission.valueAt(i);
int uid = systemPermission.keyAt(i);
- int netdPermission = PERMISSION_NONE;
+ int netdPermission = 0;
// Get the uids of native services that have UPDATE_DEVICE_STATS or INTERNET permission.
if (perms != null) {
netdPermission |= perms.contains(UPDATE_DEVICE_STATS)
- ? PERMISSION_UPDATE_DEVICE_STATS : 0;
- netdPermission |= perms.contains(INTERNET) ? PERMISSION_INTERNET : 0;
+ ? INetd.PERMISSION_UPDATE_DEVICE_STATS : 0;
+ netdPermission |= perms.contains(INTERNET)
+ ? INetd.PERMISSION_INTERNET : 0;
}
- final UidNetdPermissionInfo permInfo = netdPermsUids.get(uid);
- netdPermsUids.put(uid, permInfo != null
- ? permInfo.plusNetdPermissions(netdPermission)
- : new UidNetdPermissionInfo(netdPermission));
+ netdPermsUids.put(uid, netdPermsUids.get(uid) | netdPermission);
}
log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
update(mUsers, mApps, true);
@@ -261,34 +207,48 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
}
@VisibleForTesting
- boolean hasPermission(@NonNull final String permission, final int uid) {
- return mDeps.uidPermission(permission, uid) == PackageManager.PERMISSION_GRANTED;
+ boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) {
+ if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) {
+ return false;
+ }
+ final int index = ArrayUtils.indexOf(app.requestedPermissions, permission);
+ if (index < 0 || index >= app.requestedPermissionsFlags.length) return false;
+ return (app.requestedPermissionsFlags[index] & REQUESTED_PERMISSION_GRANTED) != 0;
}
@VisibleForTesting
- // TODO : remove this check in the future(b/162295056). All apps should just request the
- // appropriate permission for their use case since android Q.
- boolean isCarryoverPackage(@Nullable final ApplicationInfo appInfo) {
- if (appInfo == null) return false;
- return (appInfo.targetSdkVersion < VERSION_Q && isVendorApp(appInfo))
- // Backward compatibility for b/114245686, on devices that launched before Q daemons
- // and apps running as the system UID are exempted from this check.
- || (appInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q);
+ boolean hasNetworkPermission(@NonNull final PackageInfo app) {
+ return hasPermission(app, CHANGE_NETWORK_STATE);
}
@VisibleForTesting
- boolean hasRestrictedNetworkPermission(final int uid) {
- return hasPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, uid)
- || hasPermission(PERMISSION_MAINLINE_NETWORK_STACK, uid)
- || hasPermission(NETWORK_STACK, uid);
+ boolean hasRestrictedNetworkPermission(@NonNull final PackageInfo app) {
+ // TODO : remove this check in the future(b/31479477). All apps should just
+ // request the appropriate permission for their use case since android Q.
+ if (app.applicationInfo != null) {
+ // Backward compatibility for b/114245686, on devices that launched before Q daemons
+ // and apps running as the system UID are exempted from this check.
+ if (app.applicationInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q) {
+ return true;
+ }
+
+ if (app.applicationInfo.targetSdkVersion < VERSION_Q
+ && isVendorApp(app.applicationInfo)) {
+ return true;
+ }
+ }
+
+ return hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK)
+ || hasPermission(app, NETWORK_STACK)
+ || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
}
/** Returns whether the given uid has using background network permission. */
public synchronized boolean hasUseBackgroundNetworksPermission(final int uid) {
// Apps with any of the CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_INTERNAL or
// CONNECTIVITY_USE_RESTRICTED_NETWORKS permission has the permission to use background
- // networks. mApps contains the result of checks for both CHANGE_NETWORK_STATE permission
- // and hasRestrictedNetworkPermission. If uid is in the mApps list that means uid has one of
+ // networks. mApps contains the result of checks for both hasNetworkPermission and
+ // hasRestrictedNetworkPermission. If uid is in the mApps list that means uid has one of
// permissions at least.
return mApps.containsKey(uid);
}
@@ -313,11 +273,11 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
}
try {
if (add) {
- mNetd.networkSetPermissionForUser(PERMISSION_NETWORK, convertToIntArray(network));
- mNetd.networkSetPermissionForUser(PERMISSION_SYSTEM, convertToIntArray(system));
+ mNetd.networkSetPermissionForUser(INetd.PERMISSION_NETWORK, toIntArray(network));
+ mNetd.networkSetPermissionForUser(INetd.PERMISSION_SYSTEM, toIntArray(system));
} else {
- mNetd.networkClearPermissionForUser(convertToIntArray(network));
- mNetd.networkClearPermissionForUser(convertToIntArray(system));
+ mNetd.networkClearPermissionForUser(toIntArray(network));
+ mNetd.networkClearPermissionForUser(toIntArray(system));
}
} catch (RemoteException e) {
loge("Exception when updating permissions: " + e);
@@ -363,15 +323,14 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
}
@VisibleForTesting
- protected Boolean highestPermissionForUid(Boolean currentPermission, String name, int uid) {
+ protected Boolean highestPermissionForUid(Boolean currentPermission, String name) {
if (currentPermission == SYSTEM) {
return currentPermission;
}
try {
final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
- final boolean isNetwork = hasPermission(CHANGE_NETWORK_STATE, uid);
- final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(uid)
- || isCarryoverPackage(app.applicationInfo);
+ final boolean isNetwork = hasNetworkPermission(app);
+ final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
if (isNetwork || hasRestrictedPermission) {
currentPermission = hasRestrictedPermission;
}
@@ -382,15 +341,24 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
return currentPermission;
}
- private UidNetdPermissionInfo getPermissionForUid(final int uid) {
+ private int getPermissionForUid(final int uid) {
+ int permission = INetd.PERMISSION_NONE;
// Check all the packages for this UID. The UID has the permission if any of the
// packages in it has the permission.
final String[] packages = mPackageManager.getPackagesForUid(uid);
- if (packages == null || packages.length <= 0) {
+ if (packages != null && packages.length > 0) {
+ for (String name : packages) {
+ final PackageInfo app = getPackageInfo(name);
+ if (app != null && app.requestedPermissions != null) {
+ permission |= getNetdPermissionMask(app.requestedPermissions,
+ app.requestedPermissionsFlags);
+ }
+ }
+ } else {
// The last package of this uid is removed from device. Clean the package up.
- return new UidNetdPermissionInfo(PERMISSION_UNINSTALLED);
+ permission = INetd.PERMISSION_UNINSTALLED;
}
- return new UidNetdPermissionInfo(getNetdPermissionMask(uid));
+ return permission;
}
/**
@@ -407,7 +375,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
- final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName, uid);
+ final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
if (permission != mApps.get(uid)) {
mApps.put(uid, permission);
@@ -463,7 +431,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
String[] packages = mPackageManager.getPackagesForUid(uid);
if (packages != null && packages.length > 0) {
for (String name : packages) {
- permission = highestPermissionForUid(permission, name, uid);
+ permission = highestPermissionForUid(permission, name);
if (permission == SYSTEM) {
// An app with this UID still has the SYSTEM permission.
// Therefore, this UID must already have the SYSTEM permission.
@@ -499,13 +467,19 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
}
- private int getNetdPermissionMask(final int uid) {
- int permissions = PERMISSION_NONE;
- if (hasPermission(INTERNET, uid)) {
- permissions |= PERMISSION_INTERNET;
- }
- if (hasPermission(UPDATE_DEVICE_STATS, uid)) {
- permissions |= PERMISSION_UPDATE_DEVICE_STATS;
+ private static int getNetdPermissionMask(String[] requestedPermissions,
+ int[] requestedPermissionsFlags) {
+ int permissions = 0;
+ if (requestedPermissions == null || requestedPermissionsFlags == null) return permissions;
+ for (int i = 0; i < requestedPermissions.length; i++) {
+ if (requestedPermissions[i].equals(INTERNET)
+ && ((requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) != 0)) {
+ permissions |= INetd.PERMISSION_INTERNET;
+ }
+ if (requestedPermissions[i].equals(UPDATE_DEVICE_STATS)
+ && ((requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) != 0)) {
+ permissions |= INetd.PERMISSION_UPDATE_DEVICE_STATS;
+ }
}
return permissions;
}
@@ -640,28 +614,28 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
* permission information to netd.
*
* @param uid the app uid of the package installed
- * @param permissionInfo the permission info of given uid.
+ * @param permissions the permissions the app requested and netd cares about.
*
* @hide
*/
@VisibleForTesting
- void sendPackagePermissionsForUid(int uid, UidNetdPermissionInfo permissionInfo) {
- final SparseArray<UidNetdPermissionInfo> uidsPermInfo = new SparseArray<>();
- uidsPermInfo.put(uid, permissionInfo);
- sendPackagePermissionsToNetd(uidsPermInfo);
+ void sendPackagePermissionsForUid(int uid, int permissions) {
+ SparseIntArray netdPermissionsAppIds = new SparseIntArray();
+ netdPermissionsAppIds.put(uid, permissions);
+ sendPackagePermissionsToNetd(netdPermissionsAppIds);
}
/**
* Called by packageManagerService to send IPC to netd. Grant or revoke the INTERNET
* and/or UPDATE_DEVICE_STATS permission of the uids in array.
*
- * @param uidsPermInfo permission info array generated from each uid. If the uid permission is
- * PERMISSION_NONE or PERMISSION_UNINSTALLED, revoke all permissions of that
- * uid.
+ * @param netdPermissionsAppIds integer pairs of uids and the permission granted to it. If the
+ * permission is 0, revoke all permissions of that uid.
+ *
* @hide
*/
@VisibleForTesting
- void sendPackagePermissionsToNetd(final SparseArray<UidNetdPermissionInfo> uidsPermInfo) {
+ void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
if (mNetd == null) {
Log.e(TAG, "Failed to get the netd service");
return;
@@ -671,44 +645,50 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
ArrayList<Integer> noPermissionAppIds = new ArrayList<>();
ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
- for (int i = 0; i < uidsPermInfo.size(); i++) {
- final int uid = uidsPermInfo.keyAt(i);
- final UidNetdPermissionInfo permInfo = uidsPermInfo.valueAt(i);
- if (permInfo.hasNetdPermissions(
- PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS)) {
- allPermissionAppIds.add(uid);
- } else if (permInfo.hasNetdPermissions(PERMISSION_INTERNET)) {
- internetPermissionAppIds.add(uid);
- } else if (permInfo.hasNetdPermissions(PERMISSION_UPDATE_DEVICE_STATS)) {
- updateStatsPermissionAppIds.add(uid);
- } else if (permInfo.isPackageUninstalled()) {
- uninstalledAppIds.add(uid);
- } else {
- noPermissionAppIds.add(uid);
+ for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
+ int permissions = netdPermissionsAppIds.valueAt(i);
+ switch(permissions) {
+ case (INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS):
+ allPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ case INetd.PERMISSION_INTERNET:
+ internetPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ case INetd.PERMISSION_UPDATE_DEVICE_STATS:
+ updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ case INetd.PERMISSION_NONE:
+ noPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ case INetd.PERMISSION_UNINSTALLED:
+ uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
+ default:
+ Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
+ + netdPermissionsAppIds.keyAt(i));
}
}
try {
// TODO: add a lock inside netd to protect IPC trafficSetNetPermForUids()
if (allPermissionAppIds.size() != 0) {
mNetd.trafficSetNetPermForUids(
- PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS,
- convertToIntArray(allPermissionAppIds));
+ INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
+ ArrayUtils.convertToIntArray(allPermissionAppIds));
}
if (internetPermissionAppIds.size() != 0) {
- mNetd.trafficSetNetPermForUids(PERMISSION_INTERNET,
- convertToIntArray(internetPermissionAppIds));
+ mNetd.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET,
+ ArrayUtils.convertToIntArray(internetPermissionAppIds));
}
if (updateStatsPermissionAppIds.size() != 0) {
- mNetd.trafficSetNetPermForUids(PERMISSION_UPDATE_DEVICE_STATS,
- convertToIntArray(updateStatsPermissionAppIds));
+ mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS,
+ ArrayUtils.convertToIntArray(updateStatsPermissionAppIds));
}
if (noPermissionAppIds.size() != 0) {
- mNetd.trafficSetNetPermForUids(PERMISSION_NONE,
- convertToIntArray(noPermissionAppIds));
+ mNetd.trafficSetNetPermForUids(INetd.PERMISSION_NONE,
+ ArrayUtils.convertToIntArray(noPermissionAppIds));
}
if (uninstalledAppIds.size() != 0) {
- mNetd.trafficSetNetPermForUids(PERMISSION_UNINSTALLED,
- convertToIntArray(uninstalledAppIds));
+ mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UNINSTALLED,
+ ArrayUtils.convertToIntArray(uninstalledAppIds));
}
} catch (RemoteException e) {
Log.e(TAG, "Pass appId list of special permission failed." + e);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 041bedc3c575..ec12a971e445 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -210,6 +210,7 @@ public class SyncManager {
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
+ private static final boolean USE_WTF_FOR_ACCOUNT_ERROR = false;
private static final int SYNC_OP_STATE_VALID = 0;
// "1" used to include errors 3, 4 and 5 but now it's split up.
@@ -3446,7 +3447,7 @@ public class SyncManager {
if (isLoggable) {
Slog.v(TAG, " Dropping sync operation: account doesn't exist.");
}
- Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: account doesn't exist.");
+ logAccountError("SYNC_OP_STATE_INVALID: account doesn't exist.");
return SYNC_OP_STATE_INVALID_NO_ACCOUNT;
}
// Drop this sync request if it isn't syncable.
@@ -3456,14 +3457,14 @@ public class SyncManager {
Slog.v(TAG, " Dropping sync operation: "
+ "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS");
}
- Slog.wtf(TAG, "SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS");
+ logAccountError("SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS");
return SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS;
}
if (state == AuthorityInfo.NOT_SYNCABLE) {
if (isLoggable) {
Slog.v(TAG, " Dropping sync operation: isSyncable == NOT_SYNCABLE");
}
- Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: NOT_SYNCABLE");
+ logAccountError("SYNC_OP_STATE_INVALID: NOT_SYNCABLE");
return SYNC_OP_STATE_INVALID_NOT_SYNCABLE;
}
@@ -3482,12 +3483,20 @@ public class SyncManager {
if (isLoggable) {
Slog.v(TAG, " Dropping sync operation: disallowed by settings/network.");
}
- Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: disallowed by settings/network");
+ logAccountError("SYNC_OP_STATE_INVALID: disallowed by settings/network");
return SYNC_OP_STATE_INVALID_SYNC_DISABLED;
}
return SYNC_OP_STATE_VALID;
}
+ private void logAccountError(String message) {
+ if (USE_WTF_FOR_ACCOUNT_ERROR) {
+ Slog.wtf(TAG, message);
+ } else {
+ Slog.e(TAG, message);
+ }
+ }
+
private boolean dispatchSyncOperation(SyncOperation op) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "dispatchSyncOperation: we are going to sync " + op);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 0044d8936841..93352dca7ce9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -33,7 +33,7 @@ import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HA
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
-import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW;
+import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
@@ -104,6 +104,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -123,7 +124,6 @@ import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
import com.android.server.locksettings.LockSettingsStorage.PersistentData;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
@@ -155,6 +155,7 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
+import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -186,6 +187,14 @@ public class LockSettingsService extends ILockSettings.Stub {
private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts";
private static final String USER_SERIAL_NUMBER_KEY = "serial-number";
+ // Duration that LockSettingsService will store the gatekeeper password for. This allows
+ // multiple biometric enrollments without prompting the user to enter their password via
+ // ConfirmLockPassword/ConfirmLockPattern multiple times. This needs to be at least the duration
+ // from the start of the first biometric sensor's enrollment to the start of the last biometric
+ // sensor's enrollment. If biometric enrollment requests a password handle that has expired, the
+ // user's credential must be presented again, e.g. via ConfirmLockPattern/ConfirmLockPassword.
+ private static final int GK_PW_HANDLE_STORE_DURATION_MS = 10 * 60 * 1000; // 10 minutes
+
// Order of holding lock: mSeparateChallengeLock -> mSpManager -> this
// Do not call into ActivityManager while holding mSpManager lock.
private final Object mSeparateChallengeLock = new Object();
@@ -202,6 +211,8 @@ public class LockSettingsService extends ILockSettings.Stub {
private final LockSettingsStrongAuth mStrongAuth;
private final SynchronizedStrongAuthTracker mStrongAuthTracker;
private final BiometricDeferredQueue mBiometricDeferredQueue;
+ private final LongSparseArray<byte[]> mGatekeeperPasswords;
+ private final Random mRandom;
private final NotificationManager mNotificationManager;
private final UserManager mUserManager;
@@ -559,6 +570,8 @@ public class LockSettingsService extends ILockSettings.Stub {
mStorageManager = injector.getStorageManager();
mStrongAuthTracker = injector.getStrongAuthTracker();
mStrongAuthTracker.register(mStrongAuth);
+ mGatekeeperPasswords = new LongSparseArray<>();
+ mRandom = new SecureRandom();
mSpManager = injector.getSyntheticPasswordManager(mStorage);
mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache();
@@ -1017,7 +1030,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
}
- private final void checkPasswordReadPermission(int userId) {
+ private final void checkPasswordReadPermission() {
mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
}
@@ -1923,7 +1936,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public VerifyCredentialResponse checkCredential(LockscreenCredential credential, int userId,
ICheckCredentialProgressCallback progressCallback) {
- checkPasswordReadPermission(userId);
+ checkPasswordReadPermission();
try {
return doVerifyCredential(credential, userId, progressCallback, 0 /* flags */);
} finally {
@@ -1935,7 +1948,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Nullable
public VerifyCredentialResponse verifyCredential(LockscreenCredential credential,
int userId, int flags) {
- checkPasswordReadPermission(userId);
+ checkPasswordReadPermission();
try {
return doVerifyCredential(credential, userId, null /* progressCallback */, flags);
} finally {
@@ -1944,18 +1957,36 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
- public VerifyCredentialResponse verifyGatekeeperPassword(byte[] gatekeeperPassword,
+ public VerifyCredentialResponse verifyGatekeeperPasswordHandle(long gatekeeperPasswordHandle,
long challenge, int userId) {
- checkPasswordReadPermission(userId);
+ checkPasswordReadPermission();
+
+ final VerifyCredentialResponse response;
+ final byte[] gatekeeperPassword;
+
+ synchronized (mGatekeeperPasswords) {
+ gatekeeperPassword = mGatekeeperPasswords.get(gatekeeperPasswordHandle);
+ }
- VerifyCredentialResponse response;
synchronized (mSpManager) {
- response = mSpManager.verifyChallengeInternal(getGateKeeperService(),
- gatekeeperPassword, challenge, userId);
+ if (gatekeeperPassword == null) {
+ response = VerifyCredentialResponse.ERROR;
+ } else {
+ response = mSpManager.verifyChallengeInternal(getGateKeeperService(),
+ gatekeeperPassword, challenge, userId);
+ }
}
return response;
}
+ @Override
+ public void removeGatekeeperPasswordHandle(long gatekeeperPasswordHandle) {
+ checkPasswordReadPermission();
+ synchronized (mGatekeeperPasswords) {
+ mGatekeeperPasswords.remove(gatekeeperPasswordHandle);
+ }
+ }
+
/*
* Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
* format.
@@ -2012,7 +2043,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential,
int userId, @LockPatternUtils.VerifyFlag int flags) {
- checkPasswordReadPermission(userId);
+ checkPasswordReadPermission();
if (!isManagedProfileWithUnifiedLock(userId)) {
throw new IllegalArgumentException("User id must be managed profile with unified lock");
}
@@ -2179,7 +2210,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
mFirstCallToVold = false;
- checkPasswordReadPermission(userId);
+ checkPasswordReadPermission();
// There's no guarantee that this will safely connect, but if it fails
// we will simply show the lock screen when we shouldn't, so relatively
@@ -2269,13 +2300,13 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
- checkPasswordReadPermission(UserHandle.USER_ALL);
+ checkPasswordReadPermission();
mStrongAuth.registerStrongAuthTracker(tracker);
}
@Override
public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
- checkPasswordReadPermission(UserHandle.USER_ALL);
+ checkPasswordReadPermission();
mStrongAuth.unregisterStrongAuthTracker(tracker);
}
@@ -2305,7 +2336,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public int getStrongAuthForUser(int userId) {
- checkPasswordReadPermission(userId);
+ checkPasswordReadPermission();
return mStrongAuthTracker.getStrongAuthForUser(userId);
}
@@ -2648,7 +2679,7 @@ public class LockSettingsService extends ILockSettings.Stub {
final AuthenticationResult authResult;
VerifyCredentialResponse response;
- final boolean returnGkPw = (flags & VERIFY_FLAG_RETURN_GK_PW) != 0;
+ final boolean requestGkPw = (flags & VERIFY_FLAG_REQUEST_GK_PW_HANDLE) != 0;
synchronized (mSpManager) {
if (!isSyntheticPasswordBasedCredentialLocked(userId)) {
@@ -2690,14 +2721,43 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- if (response.isMatched() && returnGkPw) {
- return new VerifyCredentialResponse.Builder()
- .setGatekeeperPassword(authResult.authToken.deriveGkPassword()).build();
+ if (response.isMatched() && requestGkPw) {
+ final long handle = storeGatekeeperPasswordTemporarily(
+ authResult.authToken.deriveGkPassword());
+ return new VerifyCredentialResponse.Builder().setGatekeeperPasswordHandle(handle)
+ .build();
} else {
return response;
}
}
+ /**
+ * Stores the gatekeeper password temporarily.
+ * @param gatekeeperPassword unlocked upon successful Synthetic Password
+ * @return non-zero handle to the gatekeeper password, which can be used for a set amount of
+ * time.
+ */
+ private long storeGatekeeperPasswordTemporarily(byte[] gatekeeperPassword) {
+ long handle = 0L;
+
+ synchronized (mGatekeeperPasswords) {
+ while (handle == 0L || mGatekeeperPasswords.get(handle) != null) {
+ handle = mRandom.nextLong();
+ }
+ mGatekeeperPasswords.put(handle, gatekeeperPassword);
+ }
+
+ final long finalHandle = handle;
+ mHandler.postDelayed(() -> {
+ synchronized (mGatekeeperPasswords) {
+ Slog.d(TAG, "Removing handle: " + finalHandle);
+ mGatekeeperPasswords.remove(finalHandle);
+ }
+ }, GK_PW_HANDLE_STORE_DURATION_MS);
+
+ return handle;
+ }
+
private void onCredentialVerified(AuthenticationToken authToken, PasswordMetrics metrics,
int userId) {
@@ -2935,7 +2995,7 @@ public class LockSettingsService extends ILockSettings.Stub {
*/
@Override
public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) {
- checkPasswordReadPermission(userId);
+ checkPasswordReadPermission();
try {
if (isManagedProfileWithUnifiedLock(userId)) {
try {
@@ -3013,7 +3073,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public boolean hasPendingEscrowToken(int userId) {
- checkPasswordReadPermission(userId);
+ checkPasswordReadPermission();
synchronized (mSpManager) {
return !mSpManager.getPendingTokensForUser(userId).isEmpty();
}
@@ -3199,6 +3259,8 @@ public class LockSettingsService extends ILockSettings.Stub {
mRebootEscrowManager.dump(pw);
pw.println();
pw.decreaseIndent();
+
+ pw.println("PasswordHandleCount: " + mGatekeeperPasswords.size());
}
/**
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 0575ac6315a1..d202a2a60738 100644
--- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -100,11 +100,16 @@ public class NetworkStatsSubscriptionsMonitor extends
if (match != null) continue;
// Create listener for every newly added sub. Also store subscriberId into it to
- // prevent binder call to telephony when querying RAT.
+ // prevent binder call to telephony when querying RAT. If the subscriberId is empty
+ // for any reason, such as SIM PIN locked, skip registration.
+ // SubscriberId will be unavailable again if 1. modem crashed 2. reboot
+ // 3. re-insert SIM. If that happens, the listeners will be eventually synchronized
+ // with active sub list once all subscriberIds are ready.
final String subscriberId = mTeleManager.getSubscriberId(subId);
if (TextUtils.isEmpty(subscriberId)) {
- Log.wtf(NetworkStatsService.TAG,
- "Empty subscriberId for newly added sub: " + subId);
+ Log.d(NetworkStatsService.TAG, "Empty subscriberId for newly added sub "
+ + subId + ", skip listener registration");
+ continue;
}
final RatTypeListener listener =
new RatTypeListener(mExecutor, this, subId, subscriberId);
@@ -113,6 +118,7 @@ public class NetworkStatsSubscriptionsMonitor extends
// Register listener to the telephony manager that associated with specific sub.
mTeleManager.createForSubscriptionId(subId)
.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + subId);
}
for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
@@ -165,6 +171,7 @@ public class NetworkStatsSubscriptionsMonitor extends
private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) {
mTeleManager.createForSubscriptionId(listener.mSubId)
.listen(listener, PhoneStateListener.LISTEN_NONE);
+ Log.d(NetworkStatsService.TAG, "RAT type listener unregistered for sub " + listener.mSubId);
mRatListeners.remove(listener);
// Removal of subscriptions doesn't generate RAT changed event, fire it for every
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 9f9235dc852f..04658555f22b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6103,7 +6103,8 @@ public class NotificationManagerService extends SystemService {
protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
if (isBlocked(r)) {
if (DBG) {
- Slog.e(TAG, "Suppressing notification from package by user request.");
+ Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName()
+ + " by user request.");
}
usageStats.registerBlocked(r);
return true;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9c8b972985eb..3b98d24dc709 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -367,7 +367,6 @@ import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
-import com.android.server.pm.permission.PermissionsState;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.security.VerityUtils;
@@ -1818,7 +1817,7 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
removeMessages(WRITE_SETTINGS);
removeMessages(WRITE_PACKAGE_RESTRICTIONS);
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
mDirtyUsers.clear();
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
@@ -1838,6 +1837,7 @@ public class PackageManagerService extends IPackageManager.Stub
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_PACKAGE_LIST);
+ mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
mSettings.writePackageListLPr(msg.arg1);
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
@@ -2518,7 +2518,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
mSettings.onVolumeForgotten(fsUuid);
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
}
}
};
@@ -3442,6 +3442,7 @@ public class PackageManagerService extends IPackageManager.Stub
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
+ mPermissionManager.readPermissionsStateFromPackageSettingsTEMP();
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
@@ -3561,7 +3562,7 @@ public class PackageManagerService extends IPackageManager.Stub
// can downgrade to reader
t.traceBegin("write settings");
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
t.traceEnd();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
@@ -3765,7 +3766,7 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
}
mPermissionManager.updatePermissions(pkg.getPackageName(), pkg);
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
}
} catch (PackageManagerException e) {
// Whoops! Something went very wrong; roll back to the stub and disable the package
@@ -3776,9 +3777,8 @@ public class PackageManagerService extends IPackageManager.Stub
// If we don't, installing the system package fails during scan
enableSystemPackageLPw(stubPkg);
}
- installPackageFromSystemLIF(stubPkg.getCodePath(),
- null /*allUserHandles*/, null /*origUserHandles*/,
- null /*origPermissionsState*/, true /*writeSettings*/);
+ installPackageFromSystemLIF(stubPkg.getCodePath(), null /*allUserHandles*/,
+ null /*origUserHandles*/, true /*writeSettings*/);
} catch (PackageManagerException pme) {
// Serious WTF; we have to be able to install the stub
Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
@@ -3792,7 +3792,7 @@ public class PackageManagerService extends IPackageManager.Stub
stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
UserHandle.USER_SYSTEM, "android");
}
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
}
}
return false;
@@ -11680,8 +11680,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_ABI_SELECTION) {
Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
- + " to root=" + parsedPackage.getNativeLibraryRootDir() + ", isa="
- + parsedPackage.isNativeLibraryRootRequiresIsa());
+ + " to root=" + parsedPackage.getNativeLibraryRootDir()
+ + ", to dir=" + parsedPackage.getNativeLibraryDir()
+ + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
}
// Push the derived path down into PackageSettings so we know what to
@@ -11689,9 +11690,10 @@ public class PackageManagerService extends IPackageManager.Stub
pkgSetting.legacyNativeLibraryPathString = parsedPackage.getNativeLibraryRootDir();
if (DEBUG_ABI_SELECTION) {
- Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are" +
- " primary=" + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage) +
- " secondary=" + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
+ Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
+ + " primary=" + pkgSetting.primaryCpuAbiString
+ + " secondary=" + pkgSetting.primaryCpuAbiString
+ + " abiOverride=" + pkgSetting.cpuAbiOverrideString);
}
if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
@@ -13092,7 +13094,7 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
if (sendRemoved) {
- killApplication(packageName, UserHandle.getUid(userId, pkgSetting.appId),
+ killApplication(packageName, pkgSetting.appId, userId,
"hiding pkg");
sendApplicationHiddenForUser(packageName, pkgSetting, userId);
return true;
@@ -16280,7 +16282,7 @@ public class PackageManagerService extends IPackageManager.Stub
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
//to update install status
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -17610,12 +17612,13 @@ public class PackageManagerService extends IPackageManager.Stub
}
boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
&& pkgSetting.getPkgState().isUpdatedSystemApp();
+ final String abiOverride = deriveAbiOverride(args.abiOverride, pkgSetting);
AndroidPackage oldPackage = mPackages.get(pkgName);
boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
- args.abiOverride);
+ abiOverride);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
} catch (PackageManagerException pme) {
@@ -18877,6 +18880,10 @@ public class PackageManagerService extends IPackageManager.Stub
if (outInfo != null) {
outInfo.removedAppId = removedAppId;
}
+ if ((deletedPs.sharedUser == null || deletedPs.sharedUser.packages.size() == 0)
+ && !isUpdatedSystemApp(deletedPs)) {
+ mPermissionManager.removePermissionsStateTEMP(removedAppId);
+ }
mPermissionManager.updatePermissions(deletedPs.name, null);
if (deletedPs.sharedUser != null) {
// Remove permissions associated with package. Since runtime
@@ -18886,10 +18893,10 @@ public class PackageManagerService extends IPackageManager.Stub
// package is successful and this causes a change in gids.
boolean shouldKill = false;
for (int userId : UserManagerService.getInstance().getUserIds()) {
- final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,
- userId);
- shouldKill |= userIdToKill == UserHandle.USER_ALL
- || userIdToKill >= UserHandle.USER_SYSTEM;
+ final int userIdToKill = mPermissionManager
+ .revokeSharedUserPermissionsForDeletedPackageTEMP(deletedPs,
+ userId);
+ shouldKill |= userIdToKill != UserHandle.USER_NULL;
}
// If gids changed, kill all affected packages.
if (shouldKill) {
@@ -18933,7 +18940,7 @@ public class PackageManagerService extends IPackageManager.Stub
// can downgrade to reader
if (writeSettings) {
// Save settings now
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
}
if (installedStateChanged) {
mSettings.writeKernelMappingLPr(deletedPs);
@@ -19020,8 +19027,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
try {
installPackageFromSystemLIF(disabledPs.getCodePathString(), allUserHandles,
- outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(),
- writeSettings);
+ outInfo == null ? null : outInfo.origUsers, writeSettings);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": "
+ e.getMessage());
@@ -19052,9 +19058,8 @@ public class PackageManagerService extends IPackageManager.Stub
* Installs a package that's already on the system partition.
*/
private AndroidPackage installPackageFromSystemLIF(@NonNull String codePathString,
- @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
- @Nullable PermissionsState origPermissionState, boolean writeSettings)
- throws PackageManagerException {
+ @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings)
+ throws PackageManagerException {
final File codePath = new File(codePathString);
@ParseFlags int parseFlags =
mDefParseFlags
@@ -19091,12 +19096,8 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName());
- // Propagate the permissions state as we do not want to drop on the floor
- // runtime permissions. The update permissions method below will take
- // care of removing obsolete permissions and grant install permissions.
- if (origPermissionState != null) {
- ps.getPermissionsState().copyFrom(origPermissionState);
- }
+ // The update permissions method below will take care of removing obsolete permissions
+ // and granting install permissions.
mPermissionManager.updatePermissions(pkg.getPackageName(), pkg);
final boolean applyUserRestrictions
@@ -19130,7 +19131,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
// can downgrade to reader here
if (writeSettings) {
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
}
}
return pkg;
@@ -19204,7 +19205,7 @@ public class PackageManagerService extends IPackageManager.Stub
} else {
ps.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
}
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
}
return true;
}
@@ -20396,7 +20397,7 @@ public class PackageManagerService extends IPackageManager.Stub
(parser1, userId1) -> {
synchronized (mLock) {
mSettings.readAllDomainVerificationsLPr(parser1, userId1);
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
}
});
} catch (Exception e) {
@@ -21747,6 +21748,8 @@ public class PackageManagerService extends IPackageManager.Stub
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+ mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+
DumpState dumpState = new DumpState();
boolean fullPreferred = false;
boolean checkin = false;
@@ -21942,7 +21945,7 @@ public class PackageManagerService extends IPackageManager.Stub
dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
} else if ("write".equals(cmd)) {
synchronized (mLock) {
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
pw.println("Settings written.");
return;
}
@@ -22660,7 +22663,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Yay, everything is now upgraded
ver.forceCurrent();
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
}
for (PackageFreezer freezer : freezers) {
@@ -22710,7 +22713,7 @@ public class PackageManagerService extends IPackageManager.Stub
AttributeCache.instance().removePackage(ps.name);
}
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
}
}
@@ -23623,6 +23626,8 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
mDirtyUsers.remove(userId);
mUserNeedsBadging.delete(userId);
+ mPermissionManager.onUserRemoved(userId);
+ mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
mSettings.removeUserLPw(userId);
mPendingBroadcasts.remove(userId);
mInstantAppRegistry.onUserRemovedLPw(userId);
@@ -23723,7 +23728,9 @@ public class PackageManagerService extends IPackageManager.Stub
boolean readPermissionStateForUser(@UserIdInt int userId) {
synchronized (mPackages) {
+ mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
mSettings.readPermissionStateForUserSyncLPr(userId);
+ mPermissionManager.readPermissionsStateFromPackageSettingsTEMP();
return mPmInternal.isPermissionUpgradeNeeded(userId);
}
}
@@ -25179,7 +25186,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (async) {
scheduleWriteSettingsLocked();
} else {
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
}
}
}
@@ -25226,7 +25233,7 @@ public class PackageManagerService extends IPackageManager.Stub
return;
}
mSettings.mReadExternalStorageEnforced = enforced ? Boolean.TRUE : Boolean.FALSE;
- mSettings.writeLPr();
+ writeSettingsLPrTEMP();
}
}
@@ -25740,6 +25747,17 @@ public class PackageManagerService extends IPackageManager.Stub
public List<String> getMimeGroup(String packageName, String mimeGroup) {
return mSettings.mPackages.get(packageName).getMimeGroup(mimeGroup);
}
+
+ /**
+ * Temporary method that wraps mSettings.writeLPr() and calls
+ * mPermissionManager.writePermissionsStateToPackageSettingsTEMP() beforehand.
+ *
+ * TODO(zhanghai): This should be removed once we finish migration of permission storage.
+ */
+ private void writeSettingsLPrTEMP() {
+ mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mSettings.writeLPr();
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index de0e4b53adab..491b4fc515ce 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -34,7 +34,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.ResolveInfo;
@@ -68,7 +67,6 @@ import com.android.server.EventLogTags;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.permission.PermissionsState;
import dalvik.system.VMRuntime;
@@ -968,20 +966,6 @@ public class PackageManagerServiceUtils {
}
/**
- * Returns the {@link PermissionsState} for the given package. If the {@link PermissionsState}
- * could not be found, {@code null} will be returned.
- */
- public static PermissionsState getPermissionsState(
- PackageManagerInternal packageManagerInternal, AndroidPackage pkg) {
- final PackageSetting packageSetting = packageManagerInternal.getPackageSetting(
- pkg.getPackageName());
- if (packageSetting == null) {
- return null;
- }
- return packageSetting.getPermissionsState();
- }
-
- /**
* Recursively create target directory
*/
public static void makeDirRecursive(File targetDir, int mode) throws ErrnoException {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 32ec052a1fe0..7e2ef41943d6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3199,12 +3199,24 @@ class PackageManagerShellCommand extends ShellCommand {
return 0;
}
+ private long getFileStatSize(File file) {
+ final ParcelFileDescriptor pfd = openFileForSystem(file.getPath(), "r");
+ if (pfd == null) {
+ throw new IllegalArgumentException("Error: Can't open file: " + file.getPath());
+ }
+ try {
+ return pfd.getStatSize();
+ } finally {
+ IoUtils.closeQuietly(pfd);
+ }
+ }
+
private void processArgForLocalFile(String arg, PackageInstaller.Session session) {
final String inPath = arg;
final File file = new File(inPath);
final String name = file.getName();
- final long size = file.length();
+ final long size = getFileStatSize(file);
final Metadata metadata = Metadata.forLocalFile(inPath);
byte[] v4signatureBytes = null;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index acb149b9ec3d..659e2a32e267 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -955,93 +955,6 @@ public final class Settings {
}
}
- /*
- * Update the shared user setting when a package with a shared user id is removed. The gids
- * associated with each permission of the deleted package are removed from the shared user'
- * gid list only if its not in use by other permissions of packages in the shared user setting.
- *
- * @return the affected user id
- */
- @UserIdInt
- int updateSharedUserPermsLPw(PackageSetting deletedPs, int userId) {
- if ((deletedPs == null) || (deletedPs.pkg == null)) {
- Slog.i(PackageManagerService.TAG,
- "Trying to update info for null package. Just ignoring");
- return UserHandle.USER_NULL;
- }
-
- // No sharedUserId
- if (deletedPs.sharedUser == null) {
- return UserHandle.USER_NULL;
- }
-
- SharedUserSetting sus = deletedPs.sharedUser;
-
- int affectedUserId = UserHandle.USER_NULL;
- // Update permissions
- for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) {
- BasePermission bp = mPermissions.getPermission(eachPerm);
- if (bp == null) {
- continue;
- }
-
- // Check if another package in the shared user needs the permission.
- boolean used = false;
- for (PackageSetting pkg : sus.packages) {
- if (pkg.pkg != null
- && !pkg.pkg.getPackageName().equals(deletedPs.pkg.getPackageName())
- && pkg.pkg.getRequestedPermissions().contains(eachPerm)) {
- used = true;
- break;
- }
- }
- if (used) {
- continue;
- }
-
- PermissionsState permissionsState = sus.getPermissionsState();
- PackageSetting disabledPs = getDisabledSystemPkgLPr(deletedPs.pkg.getPackageName());
-
- // If the package is shadowing is a disabled system package,
- // do not drop permissions that the shadowed package requests.
- if (disabledPs != null) {
- boolean reqByDisabledSysPkg = false;
- for (String permission : disabledPs.pkg.getRequestedPermissions()) {
- if (permission.equals(eachPerm)) {
- reqByDisabledSysPkg = true;
- break;
- }
- }
- if (reqByDisabledSysPkg) {
- continue;
- }
- }
-
- // Try to revoke as an install permission which is for all users.
- // The package is gone - no need to keep flags for applying policy.
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
-
- if (permissionsState.revokeInstallPermission(bp) ==
- PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
- affectedUserId = UserHandle.USER_ALL;
- }
-
- // Try to revoke as an install permission which is per user.
- if (permissionsState.revokeRuntimePermission(bp, userId) ==
- PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
- if (affectedUserId == UserHandle.USER_NULL) {
- affectedUserId = userId;
- } else if (affectedUserId != userId) {
- // Multiple users affected.
- affectedUserId = UserHandle.USER_ALL;
- }
- }
- }
-
- return affectedUserId;
- }
-
int removePackageLPw(String name) {
final PackageSetting p = mPackages.get(name);
if (p != null) {
@@ -4638,6 +4551,7 @@ public final class Settings {
? "true" : "false");
pw.print(prefix); pw.print(" primaryCpuAbi="); pw.println(ps.primaryCpuAbiString);
pw.print(prefix); pw.print(" secondaryCpuAbi="); pw.println(ps.secondaryCpuAbiString);
+ pw.print(prefix); pw.print(" cpuAbiOverride="); pw.println(ps.cpuAbiOverrideString);
}
pw.print(prefix); pw.print(" versionCode="); pw.print(ps.versionCode);
if (pkg != null) {
@@ -5533,32 +5447,11 @@ public final class Settings {
// Make sure we do not
mHandler.removeMessages(userId);
- for (SettingBase sb : mPackages.values()) {
- revokeRuntimePermissionsAndClearFlags(sb, userId);
- }
-
- for (SettingBase sb : mSharedUsers.values()) {
- revokeRuntimePermissionsAndClearFlags(sb, userId);
- }
-
mPermissionUpgradeNeeded.delete(userId);
mVersions.delete(userId);
mFingerprints.remove(userId);
}
- private void revokeRuntimePermissionsAndClearFlags(SettingBase sb, int userId) {
- PermissionsState permissionsState = sb.getPermissionsState();
- for (PermissionState permissionState
- : permissionsState.getRuntimePermissionStates(userId)) {
- BasePermission bp = mPermissions.getPermission(permissionState.getName());
- if (bp != null) {
- permissionsState.revokeRuntimePermission(bp, userId);
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
- }
- }
- }
-
public void deleteUserRuntimePermissionsFile(int userId) {
mPersistence.deleteForUser(UserHandle.of(userId));
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 6d80f08ecbf6..105a9b06cf42 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -1005,7 +1005,7 @@ public class StagingManager {
// will be deleted.
}
root.setStagedSessionFailed(
- SessionInfo.STAGED_SESSION_OTHER_ERROR,
+ SessionInfo.STAGED_SESSION_CONFLICT,
"Session was blocking rollback session: " + session.sessionId);
Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to "
+ "blocking rollback session: " + session.sessionId);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d137fd05f793..c1aebd33889c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -84,6 +84,7 @@ import android.os.storage.StorageManager;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
import android.stats.devicepolicy.DevicePolicyEnums;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -141,7 +142,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* Service for {@link UserManager}.
@@ -159,6 +159,10 @@ public class UserManagerService extends IUserManager.Stub {
private static final String LOG_TAG = "UserManagerService";
static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE
+
+ // TODO(b/164159026): remove once owner_name issue on automotive is fixed
+ // Can be used to track getUsers() / userWithNameLU() behavior
+ public static final boolean DBG_CACHED_USERINFOS = false; // DO NOT SUBMIT WITH TRUE
// Can be used for manual testing of id recycling
private static final boolean RELEASE_DELETED_USER_ID = false; // DO NOT SUBMIT WITH TRUE
@@ -272,6 +276,25 @@ public class UserManagerService extends IUserManager.Stub {
private DevicePolicyManagerInternal mDevicePolicyManagerInternal;
/**
+ * Reference to the {@link UserHandle#SYSTEM} user's UserInfo; it's {@code name} was either
+ * manually set, or it's {@code null}.
+ *
+ * <p>The reference is set just once, but it's {@code name} is updated when it's manually set.
+ */
+ @GuardedBy("mUsersLock")
+ private UserInfo mSystemUserInfo;
+
+ /**
+ * Reference to the {@link UserHandle#SYSTEM} user's UserInfo, with its {@code name} set to
+ * the localized value of {@code owner_name}.
+ *
+ * <p>The reference is set just once, but it's {@code name} is updated everytime the reference
+ * is used and the locale changed.
+ */
+ @GuardedBy("mUsersLock")
+ private UserInfo mSystemUserInfoWithName;
+
+ /**
* Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
*/
@VisibleForTesting
@@ -444,11 +467,6 @@ public class UserManagerService extends IUserManager.Stub {
}
};
- // TODO(b/161915546): remove once userWithName() is fixed / removed
- // Use to debug / dump when user 0 is allocated at userWithName()
- public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE
- public final AtomicInteger mUser0Allocations;
-
/**
* Start an {@link IntentSender} when user is unlocked after disabling quiet mode.
*
@@ -638,7 +656,6 @@ public class UserManagerService extends IUserManager.Stub {
LocalServices.addService(UserManagerInternal.class, mLocalService);
mLockPatternUtils = new LockPatternUtils(mContext);
mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING);
- mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null;
}
void systemReady() {
@@ -784,7 +801,7 @@ public class UserManagerService extends IUserManager.Stub {
|| (excludePreCreated && ui.preCreated)) {
continue;
}
- users.add(userWithName(ui));
+ users.add(userWithNameLU(ui));
}
return users;
}
@@ -853,7 +870,7 @@ public class UserManagerService extends IUserManager.Stub {
userInfo.name = null;
userInfo.iconPath = null;
} else {
- userInfo = userWithName(userInfo);
+ userInfo = userWithNameLU(userInfo);
}
users.add(userInfo);
}
@@ -1310,26 +1327,57 @@ public class UserManagerService extends IUserManager.Stub {
public UserInfo getUserInfo(@UserIdInt int userId) {
checkManageOrCreateUsersPermission("query user");
synchronized (mUsersLock) {
- return userWithName(getUserInfoLU(userId));
+ return userWithNameLU(getUserInfoLU(userId));
}
}
/**
* Returns a UserInfo object with the name filled in, for Owner, or the original
* if the name is already set.
+ *
+ * <p>Note:</p> the Owner name is localized, so the current value must be checked every time
+ * this method is called.
*/
- private UserInfo userWithName(UserInfo orig) {
- if (orig != null && orig.name == null && orig.id == UserHandle.USER_SYSTEM) {
- if (DBG_ALLOCATION) {
- final int number = mUser0Allocations.incrementAndGet();
- Slog.w(LOG_TAG, "System user instantiated at least " + number + " times");
- }
- UserInfo withName = new UserInfo(orig);
- withName.name = getOwnerName();
- return withName;
- } else {
- return orig;
+ private UserInfo userWithNameLU(UserInfo orig) {
+ // Only the system user uses the owner_name string.
+ if (orig == null || orig.id != UserHandle.USER_SYSTEM) return orig;
+
+ if (mSystemUserInfo == null) {
+ mSystemUserInfo = orig;
+ if (DBG_CACHED_USERINFOS) {
+ Slog.d(LOG_TAG, "Set mSystemUserInfo:" + mSystemUserInfo.toFullString());
+ }
}
+
+ if (mSystemUserInfo.name != null) {
+ if (DBG_CACHED_USERINFOS) {
+ Slog.v(LOG_TAG, "Returning mSystemUserInfo: " + mSystemUserInfo.toFullString());
+ }
+ return mSystemUserInfo;
+ }
+
+ final String ownerName = getOwnerName();
+
+ if (mSystemUserInfoWithName == null) {
+ mSystemUserInfoWithName = new UserInfo(orig);
+ mSystemUserInfoWithName.name = ownerName;
+ if (DBG_CACHED_USERINFOS) {
+ Slog.d(LOG_TAG, "Set mSystemUserInfoWithName: "
+ + mSystemUserInfoWithName.toFullString());
+ }
+ } else if (!TextUtils.equals(ownerName, mSystemUserInfoWithName.name)) {
+ if (DBG_CACHED_USERINFOS) {
+ Slog.d(LOG_TAG, "Updating mSystemUserInfoWithName.name from "
+ + mSystemUserInfoWithName.name + " to " + ownerName);
+ }
+ mSystemUserInfoWithName.name = ownerName;
+ }
+
+ if (DBG_CACHED_USERINFOS) {
+ Slog.v(LOG_TAG, "Returning mSystemUserInfoWithName:"
+ + mSystemUserInfoWithName.toFullString());
+ }
+ return mSystemUserInfoWithName;
}
/** Returns whether the given user type is one of the FULL user types. */
@@ -1482,7 +1530,7 @@ public class UserManagerService extends IUserManager.Stub {
}
final int userId = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mUsersLock) {
- UserInfo userInfo = userWithName(getUserInfoLU(userId));
+ UserInfo userInfo = userWithNameLU(getUserInfoLU(userId));
return userInfo == null ? "" : userInfo.name;
}
}
@@ -1597,6 +1645,13 @@ public class UserManagerService extends IUserManager.Stub {
Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId);
return null;
}
+
+ if (DBG_CACHED_USERINFOS && userId == UserHandle.USER_SYSTEM && userData != null
+ && userData.info != mSystemUserInfo) {
+ Slog.wtf(LOG_TAG, "getUserInfoLU(): system user on userData (" + userData.info
+ + ") is not the same as mSystemUserInfo (" + mSystemUserInfo + ")");
+ }
+
return userData != null ? userData.info : null;
}
@@ -4855,8 +4910,15 @@ public class UserManagerService extends IUserManager.Stub {
pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
pw.println(" User version: " + mUserVersion);
pw.println(" Owner name: " + getOwnerName());
- if (DBG_ALLOCATION) {
- pw.println(" System user allocations: " + mUser0Allocations.get());
+ if (mSystemUserInfo == null) {
+ pw.println(" (mSystemUserInfo not set)");
+ } else {
+ pw.println(" System user: " + mSystemUserInfo.toFullString());
+ }
+ if (mSystemUserInfoWithName == null) {
+ pw.println(" (mSystemUserInfoWithName not set)");
+ } else {
+ pw.println(" System user (with name): " + mSystemUserInfoWithName.toFullString());
}
// Dump UserTypes
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index cfa0449aaf33..962638b4f63c 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -38,7 +38,6 @@ import android.util.Slog;
import com.android.server.pm.DumpState;
import com.android.server.pm.PackageManagerService;
-import com.android.server.pm.PackageSetting;
import com.android.server.pm.PackageSettingBase;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -420,8 +419,7 @@ public final class BasePermission {
}
public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg,
- PackageSetting pkgSetting) {
- final PermissionsState permsState = pkgSetting.getPermissionsState();
+ PermissionsState permsState) {
int index = pkg.getRequestedPermissions().indexOf(name);
if (!permsState.hasRequestedPermission(name) && index == -1) {
throw new SecurityException("Package " + pkg.getPackageName()
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 1be74154b53a..f5dd918a18f3 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -162,6 +162,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -227,6 +228,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
/** Internal connection to the user manager */
private final UserManagerInternal mUserManagerInt;
+ /** Maps from App ID to PermissionsState */
+ private final SparseArray<PermissionsState> mAppIdStates = new SparseArray<>();
+
/** Permission controller: User space permission management */
private PermissionControllerManager mPermissionControllerManager;
@@ -671,11 +675,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (pkg == null) {
return 0;
}
- final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
- pkg.getPackageName());
- if (ps == null) {
- return 0;
- }
synchronized (mLock) {
if (mSettings.getPermissionLocked(permName) == null) {
return 0;
@@ -684,7 +683,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
return 0;
}
- PermissionsState permissionsState = ps.getPermissionsState();
+ final PermissionsState permissionsState = getPermissionsState(pkg);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName);
+ return 0;
+ }
return permissionsState.getPermissionFlags(permName, userId);
}
@@ -771,9 +774,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
- final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
- packageName);
- if (pkg == null || ps == null) {
+ if (pkg == null) {
Log.e(TAG, "Unknown package: " + packageName);
return;
}
@@ -789,7 +790,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
throw new IllegalArgumentException("Unknown permission: " + permName);
}
- final PermissionsState permissionsState = ps.getPermissionsState();
+ final PermissionsState permissionsState = getPermissionsState(pkg);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName);
+ return;
+ }
+
final boolean hadState =
permissionsState.getRuntimePermissionState(permName, userId) != null;
if (!hadState) {
@@ -864,12 +870,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final boolean[] changed = new boolean[1];
mPackageManagerInt.forEachPackage(pkg -> {
- final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
- pkg.getPackageName());
- if (ps == null) {
+ final PermissionsState permissionsState = getPermissionsState(pkg);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
return;
}
- final PermissionsState permissionsState = ps.getPermissionsState();
changed[0] |= permissionsState.updatePermissionFlagsForAllPermissions(
userId, effectiveFlagMask, effectiveFlagValues);
mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid());
@@ -923,12 +928,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
final int uid = UserHandle.getUid(userId, pkg.getUid());
- final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
- pkg.getPackageName());
- if (ps == null) {
+ final PermissionsState permissionsState = getPermissionsState(pkg);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
return PackageManager.PERMISSION_DENIED;
}
- final PermissionsState permissionsState = ps.getPermissionsState();
if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) {
return PackageManager.PERMISSION_GRANTED;
@@ -1139,9 +1143,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final long identity = Binder.clearCallingIdentity();
try {
- final PermissionsState permissionsState =
- PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg);
+ final PermissionsState permissionsState = getPermissionsState(pkg);
if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName);
return null;
}
@@ -1451,7 +1455,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, ps);
+ final PermissionsState permissionsState = getPermissionsState(pkg);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ return;
+ }
+
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState);
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
@@ -1464,8 +1474,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
- final PermissionsState permissionsState = ps.getPermissionsState();
-
final int flags = permissionsState.getPermissionFlags(permName, userId);
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
Log.e(TAG, "Cannot grant system fixed permission "
@@ -1599,9 +1607,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
"revokeRuntimePermission");
final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
- final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
- packageName);
- if (pkg == null || ps == null) {
+ if (pkg == null) {
Log.e(TAG, "Unknown package: " + packageName);
return;
}
@@ -1613,7 +1619,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
throw new IllegalArgumentException("Unknown permission: " + permName);
}
- bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, ps);
+ final PermissionsState permissionsState = getPermissionsState(pkg);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ return;
+ }
+
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState);
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
@@ -1624,8 +1636,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return;
}
- final PermissionsState permissionsState = ps.getPermissionsState();
-
final int flags = permissionsState.getPermissionFlags(permName, userId);
// Only the system may revoke SYSTEM_FIXED permissions.
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
@@ -2456,14 +2466,36 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
+ private void onUserRemoved(@UserIdInt int userId) {
+ synchronized (mLock) {
+ final int appIdStatesSize = mAppIdStates.size();
+ for (int i = 0; i < appIdStatesSize; i++) {
+ PermissionsState permissionsState = mAppIdStates.valueAt(i);
+ for (PermissionState permissionState
+ : permissionsState.getRuntimePermissionStates(userId)) {
+ BasePermission bp = mSettings.getPermission(permissionState.getName());
+ if (bp != null) {
+ permissionsState.revokeRuntimePermission(bp, userId);
+ permissionsState.updatePermissionFlags(bp, userId,
+ PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
+ }
+ }
+ }
+ }
+ }
+
@NonNull
private Set<String> getGrantedPermissions(@NonNull String packageName,
@UserIdInt int userId) {
final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName);
if (ps == null) {
- return null;
+ return Collections.emptySet();
+ }
+ final PermissionsState permissionsState = getPermissionsState(ps);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName);
+ return Collections.emptySet();
}
- final PermissionsState permissionsState = ps.getPermissionsState();
if (!ps.getInstantApp(userId)) {
return permissionsState.getPermissions(userId);
} else {
@@ -2503,7 +2535,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (ps == null) {
return null;
}
- final PermissionsState permissionsState = ps.getPermissionsState();
+ final PermissionsState permissionsState = getPermissionsState(ps);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName);
+ return null;
+ }
return permissionsState.computeGids(userId);
}
@@ -2541,8 +2577,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (ps == null) {
return;
}
-
- final PermissionsState permissionsState = ps.getPermissionsState();
+ final PermissionsState permissionsState = getOrCreatePermissionsState(ps);
final int[] userIds = getAllUserIds();
@@ -2614,8 +2649,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// changed runtime permissions here are promotion of an install to
// runtime and revocation of a runtime from a shared user.
synchronized (mLock) {
- updatedUserIds = revokeUnusedSharedUserPermissionsLocked(ps.getSharedUser(),
- userIds);
+ updatedUserIds = revokeUnusedSharedUserPermissionsLocked(
+ ps.getSharedUser().getPackages(), permissionsState, userIds);
if (!ArrayUtils.isEmpty(updatedUserIds)) {
runtimePermissionsRevoked = true;
}
@@ -3091,6 +3126,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
updatedUserIds);
}
+ // TODO: Kill UIDs whose GIDs or runtime permissions changed. This might be more important
+ // for shared users.
// Persist the runtime permissions state for users with changes. If permissions
// were revoked because no app in the shared user declares them we have to
// write synchronously to avoid losing runtime permissions state.
@@ -3554,37 +3591,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final PackageSetting disabledPs = mPackageManagerInt
.getDisabledSystemPackage(pkg.getPackageName());
final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg;
- if (disabledPs != null
- && disabledPs.getPermissionsState().hasInstallPermission(perm)) {
- // If the original was granted this permission, we take
- // that grant decision as read and propagate it to the
- // update.
- if ((privilegedPermission && disabledPs.isPrivileged())
- || (oemPermission && disabledPs.isOem()
- && canGrantOemPermission(disabledPs, perm))) {
- allowed = true;
- }
- } else {
- // The system apk may have been updated with an older
- // version of the one on the data partition, but which
- // granted a new system permission that it didn't have
- // before. In this case we do want to allow the app to
- // now get the new permission if the ancestral apk is
- // privileged to get it.
- if (disabledPs != null && disabledPkg != null
- && isPackageRequestingPermission(disabledPkg, perm)
- && ((privilegedPermission && disabledPs.isPrivileged())
- || (oemPermission && disabledPs.isOem()
- && canGrantOemPermission(disabledPs, perm)))) {
- allowed = true;
- }
+ if (disabledPkg != null && isPackageRequestingPermission(disabledPkg, perm)
+ && ((privilegedPermission && disabledPkg.isPrivileged())
+ || (oemPermission && canGrantOemPermission(disabledPkg,
+ perm)))) {
+ allowed = true;
}
} else {
- final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
- pkg.getPackageName());
allowed = (privilegedPermission && pkg.isPrivileged())
- || (oemPermission && pkg.isOem()
- && canGrantOemPermission(ps, perm));
+ || (oemPermission && canGrantOemPermission(pkg, perm));
}
// In any case, don't grant a privileged permission to privileged vendor apps, if
// the permission's protectionLevel does not have the extra 'vendorPrivileged'
@@ -3735,16 +3750,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return false;
}
- private static boolean canGrantOemPermission(PackageSetting ps, String permission) {
- if (!ps.isOem()) {
+ private static boolean canGrantOemPermission(AndroidPackage pkg, String permission) {
+ if (!pkg.isOem()) {
return false;
}
// all oem permissions must explicitly be granted or denied
final Boolean granted =
- SystemConfig.getInstance().getOemPermissions(ps.name).get(permission);
+ SystemConfig.getInstance().getOemPermissions(pkg.getPackageName()).get(permission);
if (granted == null) {
throw new IllegalStateException("OEM permission" + permission + " requested by package "
- + ps.name + " must be explicitly declared granted or not");
+ + pkg.getPackageName() + " must be explicitly declared granted or not");
}
return Boolean.TRUE == granted;
}
@@ -3757,12 +3772,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
// Legacy apps have the permission and get user consent on launch.
- final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
- pkg.getPackageName());
- if (ps == null) {
+ final PermissionsState permissionsState = getPermissionsState(pkg);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
return false;
}
- final PermissionsState permissionsState = ps.getPermissionsState();
return permissionsState.isPermissionReviewRequired(userId);
}
@@ -3787,14 +3801,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId,
String[] grantedPermissions, int callingUid, PermissionCallback callback) {
- PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
- pkg.getPackageName());
- if (ps == null) {
+ final PermissionsState permissionsState = getPermissionsState(pkg);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
return;
}
- PermissionsState permissionsState = ps.getPermissionsState();
-
final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
| PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -3838,9 +3850,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private void setWhitelistedRestrictedPermissionsForUsers(@NonNull AndroidPackage pkg,
@UserIdInt int[] userIds, @Nullable List<String> permissions, int callingUid,
@PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) {
- final PermissionsState permissionsState =
- PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg);
+ final PermissionsState permissionsState = getPermissionsState(pkg);
if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
return;
}
@@ -3958,9 +3970,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int j = 0; j < oldGrantedCount; j++) {
final String permission = oldPermsForUser.valueAt(j);
// Sometimes we create a new permission state instance during update.
- final PermissionsState newPermissionsState =
- PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt,
- pkg);
+ final PermissionsState newPermissionsState = getPermissionsState(pkg);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ continue;
+ }
if (!newPermissionsState.hasPermission(permission, userId)) {
callback.onPermissionRevoked(pkg.getUid(), userId, null);
break;
@@ -3970,12 +3984,100 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
+ @UserIdInt
+ private int revokeSharedUserPermissionsForDeletedPackage(@NonNull PackageSetting deletedPs,
+ @UserIdInt int userId) {
+ if ((deletedPs == null) || (deletedPs.pkg == null)) {
+ Slog.i(TAG, "Trying to update info for null package. Just ignoring");
+ return UserHandle.USER_NULL;
+ }
+
+ SharedUserSetting sus = deletedPs.getSharedUser();
+
+ // No sharedUserId
+ if (sus == null) {
+ return UserHandle.USER_NULL;
+ }
+
+ int affectedUserId = UserHandle.USER_NULL;
+ // Update permissions
+ for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) {
+ BasePermission bp = mSettings.getPermission(eachPerm);
+ if (bp == null) {
+ continue;
+ }
+
+ // Check if another package in the shared user needs the permission.
+ boolean used = false;
+ final List<AndroidPackage> pkgs = sus.getPackages();
+ if (pkgs != null) {
+ for (AndroidPackage pkg : pkgs) {
+ if (pkg != null
+ && !pkg.getPackageName().equals(deletedPs.pkg.getPackageName())
+ && pkg.getRequestedPermissions().contains(eachPerm)) {
+ used = true;
+ break;
+ }
+ }
+ }
+ if (used) {
+ continue;
+ }
+
+ PermissionsState permissionsState = getPermissionsState(deletedPs.pkg);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName());
+ continue;
+ }
+
+ PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage(
+ deletedPs.pkg.getPackageName());
+
+ // If the package is shadowing is a disabled system package,
+ // do not drop permissions that the shadowed package requests.
+ if (disabledPs != null) {
+ boolean reqByDisabledSysPkg = false;
+ for (String permission : disabledPs.pkg.getRequestedPermissions()) {
+ if (permission.equals(eachPerm)) {
+ reqByDisabledSysPkg = true;
+ break;
+ }
+ }
+ if (reqByDisabledSysPkg) {
+ continue;
+ }
+ }
+
+ // Try to revoke as an install permission which is for all users.
+ // The package is gone - no need to keep flags for applying policy.
+ permissionsState.updatePermissionFlags(bp, userId,
+ PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
+
+ if (permissionsState.revokeInstallPermission(bp)
+ == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+ affectedUserId = UserHandle.USER_ALL;
+ }
+
+ // Try to revoke as a runtime permission which is per user.
+ if (permissionsState.revokeRuntimePermission(bp, userId)
+ == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+ if (affectedUserId == UserHandle.USER_NULL) {
+ affectedUserId = userId;
+ } else if (affectedUserId != userId) {
+ // Multiple users affected.
+ affectedUserId = UserHandle.USER_ALL;
+ }
+ }
+ }
+
+ return affectedUserId;
+ }
+
@GuardedBy("mLock")
private int[] revokeUnusedSharedUserPermissionsLocked(
- SharedUserSetting suSetting, int[] allUserIds) {
+ List<AndroidPackage> pkgList, PermissionsState permissionsState, int[] allUserIds) {
// Collect all used permissions in the UID
final ArraySet<String> usedPermissions = new ArraySet<>();
- final List<AndroidPackage> pkgList = suSetting.getPackages();
if (pkgList == null || pkgList.size() == 0) {
return EmptyArray.INT;
}
@@ -3993,7 +4095,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- PermissionsState permissionsState = suSetting.getPermissionsState();
// Prune install permissions
List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates();
final int installPermCount = installPermStates.size();
@@ -4279,12 +4380,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
} else {
mPackageManagerInt.forEachPackage(p -> {
- PackageSetting ps = mPackageManagerInt.getPackageSetting(
- p.getPackageName());
- if (ps == null) {
+ final PermissionsState permissionsState = getPermissionsState(p);
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + p.getPackageName());
return;
}
- PermissionsState permissionsState = ps.getPermissionsState();
if (permissionsState.getInstallPermissionState(bp.getName()) != null) {
permissionsState.revokeInstallPermission(bp);
permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
@@ -4695,6 +4795,67 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return mBackgroundPermissions;
}
+ @Nullable
+ private PermissionsState getPermissionsState(@NonNull PackageSetting ps) {
+ return getPermissionsState(ps.getAppId());
+ }
+
+ @Nullable
+ private PermissionsState getPermissionsState(@NonNull AndroidPackage pkg) {
+ return getPermissionsState(pkg.getUid());
+ }
+
+ @Nullable
+ private PermissionsState getPermissionsState(int appId) {
+ synchronized (mLock) {
+ return mAppIdStates.get(appId);
+ }
+ }
+
+ @Nullable
+ private PermissionsState getOrCreatePermissionsState(@NonNull PackageSetting ps) {
+ return getOrCreatePermissionsState(ps.getAppId());
+ }
+
+ @Nullable
+ private PermissionsState getOrCreatePermissionsState(int appId) {
+ synchronized (mLock) {
+ PermissionsState state = mAppIdStates.get(appId);
+ if (state == null) {
+ state = new PermissionsState();
+ mAppIdStates.put(appId, state);
+ }
+ return state;
+ }
+ }
+
+ private void removePermissionsState(int appId) {
+ synchronized (mLock) {
+ mAppIdStates.remove(appId);
+ }
+ }
+
+ private void readPermissionsStateFromPackageSettings() {
+ mPackageManagerInt.forEachPackageSetting(ps -> {
+ synchronized (mLock) {
+ mAppIdStates.put(ps.getAppId(), new PermissionsState(ps.getPermissionsState()));
+ }
+ });
+ }
+
+ private void writePermissionsStateToPackageSettings() {
+ mPackageManagerInt.forEachPackageSetting(ps -> {
+ synchronized (mLock) {
+ final PermissionsState permissionsState = mAppIdStates.get(ps.getAppId());
+ if (permissionsState == null) {
+ Slog.e(TAG, "Missing permissions state for " + ps.name);
+ return;
+ }
+ ps.getPermissionsState().copyFrom(permissionsState);
+ }
+ });
+ }
+
private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal {
@Override
public void systemReady() {
@@ -4726,6 +4887,29 @@ public class PermissionManagerService extends IPermissionManager.Stub {
public void removeAllPermissions(AndroidPackage pkg, boolean chatty) {
PermissionManagerService.this.removeAllPermissions(pkg, chatty);
}
+ @Override
+ public void readPermissionsStateFromPackageSettingsTEMP() {
+ PermissionManagerService.this.readPermissionsStateFromPackageSettings();
+ }
+ @Override
+ public void writePermissionsStateToPackageSettingsTEMP() {
+ PermissionManagerService.this.writePermissionsStateToPackageSettings();
+ }
+ @Override
+ public void onUserRemoved(@UserIdInt int userId) {
+ PermissionManagerService.this.onUserRemoved(userId);
+ }
+ @Override
+ public void removePermissionsStateTEMP(int appId) {
+ PermissionManagerService.this.removePermissionsState(appId);
+ }
+ @Override
+ @UserIdInt
+ public int revokeSharedUserPermissionsForDeletedPackageTEMP(
+ @NonNull PackageSetting deletedPs, @UserIdInt int userId) {
+ return PermissionManagerService.this.revokeSharedUserPermissionsForDeletedPackage(
+ deletedPs, userId);
+ }
@NonNull
@Override
public Set<String> getGrantedPermissions(@NonNull String packageName,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index cfa371ddbad3..f319bf495e8b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -24,6 +24,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.permission.PermissionManagerInternal;
+import com.android.server.pm.PackageSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.util.ArrayList;
@@ -265,6 +266,52 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
/**
+ * Read {@code PermissionsState} from package settings.
+ *
+ * TODO(zhanghai): This is a temporary method because we should not expose
+ * {@code PackageSetting} which is a implementation detail that permission should not know.
+ * Instead, it should retrieve the legacy state via a defined API.
+ */
+ public abstract void readPermissionsStateFromPackageSettingsTEMP();
+
+ /**
+ * Write {@code PermissionsState} from to settings.
+ *
+ * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence
+ * for permission.
+ */
+ public abstract void writePermissionsStateToPackageSettingsTEMP();
+
+ /**
+ * Notify that a user has been removed and its permission state should be removed as well.
+ */
+ public abstract void onUserRemoved(@UserIdInt int userId);
+
+ /**
+ * Remove the {@code PermissionsState} associated with an app ID, called the same time as the
+ * removal of a {@code PackageSetitng}.
+ *
+ * TODO(zhanghai): This is a temporary method before we figure out a way to get notified of app
+ * ID removal via API.
+ */
+ public abstract void removePermissionsStateTEMP(int appId);
+
+ /**
+ * Update the shared user setting when a package with a shared user id is removed. The gids
+ * associated with each permission of the deleted package are removed from the shared user'
+ * gid list only if its not in use by other permissions of packages in the shared user setting.
+ *
+ * TODO(zhanghai): We should not need this when permission no longer sees an incomplete package
+ * state where the updated system package is uninstalled but the disabled system package is yet
+ * to be installed. Then we should handle this in restorePermissionState().
+ *
+ * @return the affected user id, may be a real user ID, USER_ALL, or USER_NULL when none.
+ */
+ @UserIdInt
+ public abstract int revokeSharedUserPermissionsForDeletedPackageTEMP(
+ @NonNull PackageSetting deletedPs, @UserIdInt int userId);
+
+ /**
* Get all the permissions granted to a package.
*/
@NonNull
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 8f0fd607f928..d4375eb2935b 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -94,6 +94,7 @@ public class Notifier {
private static final int MSG_USER_ACTIVITY = 1;
private static final int MSG_BROADCAST = 2;
private static final int MSG_WIRELESS_CHARGING_STARTED = 3;
+ private static final int MSG_BROADCAST_ENHANCED_PREDICTION = 4;
private static final int MSG_PROFILE_TIMED_OUT = 5;
private static final int MSG_WIRED_CHARGING_STARTED = 6;
@@ -701,6 +702,16 @@ public class Notifier {
mPolicy.userActivity();
}
+ void postEnhancedDischargePredictionBroadcast(long delayMs) {
+ mHandler.sendEmptyMessageDelayed(MSG_BROADCAST_ENHANCED_PREDICTION, delayMs);
+ }
+
+ private void sendEnhancedDischargePredictionBroadcast() {
+ Intent intent = new Intent(PowerManager.ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
private void sendNextBroadcast() {
final int powerState;
synchronized (mLock) {
@@ -872,6 +883,10 @@ public class Notifier {
case MSG_WIRELESS_CHARGING_STARTED:
showWirelessChargingStarted(msg.arg1, msg.arg2);
break;
+ case MSG_BROADCAST_ENHANCED_PREDICTION:
+ removeMessages(MSG_BROADCAST_ENHANCED_PREDICTION);
+ sendEnhancedDischargePredictionBroadcast();
+ break;
case MSG_PROFILE_TIMED_OUT:
lockProfile(msg.arg1);
break;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 882ed1b7960b..9ff164ac4a7b 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -56,6 +56,7 @@ import android.os.IBinder;
import android.os.IPowerManager;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelDuration;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManager.WakeData;
@@ -89,11 +90,13 @@ import android.view.Display;
import android.view.KeyEvent;
import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.LockGuard;
import com.android.server.RescueParty;
@@ -238,6 +241,18 @@ public final class PowerManagerService extends SystemService
private static final int HALT_MODE_REBOOT = 1;
private static final int HALT_MODE_REBOOT_SAFE_MODE = 2;
+ /**
+ * How stale we'll allow the enhanced discharge prediction values to get before considering them
+ * invalid.
+ */
+ private static final long ENHANCED_DISCHARGE_PREDICTION_TIMEOUT_MS = 30 * 60 * 1000L;
+
+ /**
+ * The minimum amount of time between sending consequent
+ * {@link PowerManager#ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED} broadcasts.
+ */
+ private static final long ENHANCED_DISCHARGE_PREDICTION_BROADCAST_MIN_DELAY_MS = 60 * 1000L;
+
private final Context mContext;
private final ServiceThread mHandlerThread;
private final Handler mHandler;
@@ -380,6 +395,34 @@ public final class PowerManagerService extends SystemService
// The current battery level percentage.
private int mBatteryLevel;
+ /**
+ * The lock that should be held when interacting with {@link #mEnhancedDischargeTimeElapsed},
+ * {@link #mLastEnhancedDischargeTimeUpdatedElapsed}, and
+ * {@link #mEnhancedDischargePredictionIsPersonalized}.
+ */
+ private final Object mEnhancedDischargeTimeLock = new Object();
+
+ /**
+ * The time (in the elapsed realtime timebase) at which the battery level will reach 0%. This
+ * is provided as an enhanced estimate and only valid if
+ * {@link #mLastEnhancedDischargeTimeUpdatedElapsed} is greater than 0.
+ */
+ @GuardedBy("mEnhancedDischargeTimeLock")
+ private long mEnhancedDischargeTimeElapsed;
+
+ /**
+ * Timestamp (in the elapsed realtime timebase) of last update to enhanced battery estimate
+ * data.
+ */
+ @GuardedBy("mEnhancedDischargeTimeLock")
+ private long mLastEnhancedDischargeTimeUpdatedElapsed;
+
+ /**
+ * Whether or not the current enhanced discharge prediction is personalized to the user.
+ */
+ @GuardedBy("mEnhancedDischargeTimeLock")
+ private boolean mEnhancedDischargePredictionIsPersonalized;
+
// The battery level percentage at the time the dream started.
// This is used to terminate a dream and go to sleep if the battery is
// draining faster than it is charging and the user activity timeout has expired.
@@ -3737,6 +3780,13 @@ public final class PowerManagerService extends SystemService
pw.println(" mProximityPositive=" + mProximityPositive);
pw.println(" mBootCompleted=" + mBootCompleted);
pw.println(" mSystemReady=" + mSystemReady);
+ synchronized (mEnhancedDischargeTimeLock) {
+ pw.println(" mEnhancedDischargeTimeElapsed=" + mEnhancedDischargeTimeElapsed);
+ pw.println(" mLastEnhancedDischargeTimeUpdatedElapsed="
+ + mLastEnhancedDischargeTimeUpdatedElapsed);
+ pw.println(" mEnhancedDischargePredictionIsPersonalized="
+ + mEnhancedDischargePredictionIsPersonalized);
+ }
pw.println(" mHalAutoSuspendModeEnabled=" + mHalAutoSuspendModeEnabled);
pw.println(" mHalInteractiveModeEnabled=" + mHalInteractiveModeEnabled);
pw.println(" mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
@@ -3952,6 +4002,16 @@ public final class PowerManagerService extends SystemService
proto.write(PowerManagerServiceDumpProto.IS_PROXIMITY_POSITIVE, mProximityPositive);
proto.write(PowerManagerServiceDumpProto.IS_BOOT_COMPLETED, mBootCompleted);
proto.write(PowerManagerServiceDumpProto.IS_SYSTEM_READY, mSystemReady);
+ synchronized (mEnhancedDischargeTimeLock) {
+ proto.write(PowerManagerServiceDumpProto.ENHANCED_DISCHARGE_TIME_ELAPSED,
+ mEnhancedDischargeTimeElapsed);
+ proto.write(
+ PowerManagerServiceDumpProto.LAST_ENHANCED_DISCHARGE_TIME_UPDATED_ELAPSED,
+ mLastEnhancedDischargeTimeUpdatedElapsed);
+ proto.write(
+ PowerManagerServiceDumpProto.IS_ENHANCED_DISCHARGE_PREDICTION_PERSONALIZED,
+ mEnhancedDischargePredictionIsPersonalized);
+ }
proto.write(
PowerManagerServiceDumpProto.IS_HAL_AUTO_SUSPEND_MODE_ENABLED,
mHalAutoSuspendModeEnabled);
@@ -3959,7 +4019,8 @@ public final class PowerManagerService extends SystemService
PowerManagerServiceDumpProto.IS_HAL_AUTO_INTERACTIVE_MODE_ENABLED,
mHalInteractiveModeEnabled);
- final long activeWakeLocksToken = proto.start(PowerManagerServiceDumpProto.ACTIVE_WAKE_LOCKS);
+ final long activeWakeLocksToken = proto.start(
+ PowerManagerServiceDumpProto.ACTIVE_WAKE_LOCKS);
proto.write(
PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_CPU,
(mWakeLockSummary & WAKE_LOCK_CPU) != 0);
@@ -4262,6 +4323,7 @@ public final class PowerManagerService extends SystemService
if (wcd != null) {
wcd.dumpDebug(proto, PowerManagerServiceDumpProto.WIRELESS_CHARGER_DETECTOR);
}
+
proto.flush();
}
@@ -5022,6 +5084,96 @@ public final class PowerManagerService extends SystemService
}
@Override // Binder call
+ public void setBatteryDischargePrediction(@NonNull ParcelDuration timeRemaining,
+ boolean isPersonalized) {
+ // Get current time before acquiring the lock so that the calculated end time is as
+ // accurate as possible.
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ final long timeRemainingMs = timeRemaining.getDuration().toMillis();
+ // A non-positive number means the battery should be dead right now...
+ Preconditions.checkArgumentPositive(timeRemainingMs,
+ "Given time remaining is not positive: " + timeRemainingMs);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (mIsPowered) {
+ throw new IllegalStateException(
+ "Discharge prediction can't be set while the device is charging");
+ }
+ }
+
+ final long broadcastDelayMs;
+ synchronized (mEnhancedDischargeTimeLock) {
+ if (mLastEnhancedDischargeTimeUpdatedElapsed > nowElapsed) {
+ // Another later call made it into the block first. Keep the latest info.
+ return;
+ }
+ broadcastDelayMs = Math.max(0,
+ ENHANCED_DISCHARGE_PREDICTION_BROADCAST_MIN_DELAY_MS
+ - (nowElapsed - mLastEnhancedDischargeTimeUpdatedElapsed));
+
+ // No need to persist the discharge prediction values since they'll most likely
+ // be wrong immediately after a reboot anyway.
+ mEnhancedDischargeTimeElapsed = nowElapsed + timeRemainingMs;
+ mEnhancedDischargePredictionIsPersonalized = isPersonalized;
+ mLastEnhancedDischargeTimeUpdatedElapsed = nowElapsed;
+ }
+ mNotifier.postEnhancedDischargePredictionBroadcast(broadcastDelayMs);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private boolean isEnhancedDischargePredictionValidLocked(long nowElapsed) {
+ return mLastEnhancedDischargeTimeUpdatedElapsed > 0
+ && nowElapsed < mEnhancedDischargeTimeElapsed
+ && nowElapsed - mLastEnhancedDischargeTimeUpdatedElapsed
+ < ENHANCED_DISCHARGE_PREDICTION_TIMEOUT_MS;
+ }
+
+ @Override // Binder call
+ public ParcelDuration getBatteryDischargePrediction() {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (mIsPowered) {
+ return null;
+ }
+ }
+ synchronized (mEnhancedDischargeTimeLock) {
+ // Get current time after acquiring the lock so that the calculated duration
+ // is as accurate as possible.
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ if (isEnhancedDischargePredictionValidLocked(nowElapsed)) {
+ return new ParcelDuration(mEnhancedDischargeTimeElapsed - nowElapsed);
+ }
+ }
+ return new ParcelDuration(mBatteryStats.computeBatteryTimeRemaining());
+ } catch (RemoteException e) {
+ // Shouldn't happen in-process.
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return null;
+ }
+
+ @Override // Binder call
+ public boolean isBatteryDischargePredictionPersonalized() {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mEnhancedDischargeTimeLock) {
+ return isEnhancedDischargePredictionValidLocked(SystemClock.elapsedRealtime())
+ && mEnhancedDischargePredictionIsPersonalized;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public boolean isDeviceIdleMode() {
final long ident = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 6dc1d6921dbb..0abeac890df1 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -336,11 +336,12 @@ public final class StorageSessionController {
}
/**
- * Returns {@code true} if {@code vol} is an emulated or public volume,
+ * Returns {@code true} if {@code vol} is an emulated or visible public volume,
* {@code false} otherwise
**/
public static boolean isEmulatedOrPublic(VolumeInfo vol) {
- return vol.type == VolumeInfo.TYPE_EMULATED || vol.type == VolumeInfo.TYPE_PUBLIC;
+ return vol.type == VolumeInfo.TYPE_EMULATED
+ || (vol.type == VolumeInfo.TYPE_PUBLIC && vol.isVisible());
}
/** Exception thrown when communication with the {@link ExternalStorageService} fails. */
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 64fa6ca590d2..9316c4657826 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4664,7 +4664,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// case where this is the top activity in a pinned stack.
final boolean isTop = this == stack.getTopNonFinishingActivity();
final boolean isTopNotPinnedStack = stack.isAttached()
- && stack.getDisplayArea().isTopNotPinnedStack(stack);
+ && stack.getDisplayArea().isTopNotFinishNotPinnedStack(stack);
final boolean visibleIgnoringDisplayStatus = stack.checkKeyguardVisibility(this,
visibleIgnoringKeyguard, isTop && isTopNotPinnedStack);
@@ -4806,6 +4806,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
Slog.v(TAG_VISIBILITY, "Start visible activity, " + this);
}
setState(STARTED, "makeActiveIfNeeded");
+
+ // Update process info while making an activity from invisible to visible, to make
+ // sure the process state is updated to foreground.
+ if (app != null) {
+ app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* activityChange */, true /* updateOomAdj */,
+ true /* addPendingTopUid */);
+ }
+
try {
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
StartActivityItem.obtain());
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 2e03cb80b189..40fc25b41d9f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -246,6 +246,9 @@ public class DisplayPolicy {
| View.STATUS_BAR_TRANSPARENT
| View.NAVIGATION_BAR_TRANSPARENT;
+ private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR};
+ private static final int[] SHOW_TYPES_FOR_PANIC = {ITYPE_NAVIGATION_BAR};
+
private final WindowManagerService mService;
private final Context mContext;
private final Context mUiContext;
@@ -3353,8 +3356,15 @@ public class DisplayPolicy {
return;
}
+ final InsetsState requestedState = controlTarget.getRequestedInsetsState();
+ final @InsetsType int restorePositionTypes =
+ (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+ ? Type.navigationBars() : 0)
+ | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
+ ? Type.statusBars() : 0);
+
if (swipeTarget == mNavigationBar
- && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) {
+ && (restorePositionTypes & Type.navigationBars()) != 0) {
// Don't show status bar when swiping on already visible navigation bar.
// But restore the position of navigation bar if it has been moved by the control
// target.
@@ -3362,14 +3372,13 @@ public class DisplayPolicy {
return;
}
- int insetsTypesToShow = Type.systemBars();
-
if (controlTarget.canShowTransient()) {
- insetsTypesToShow &= ~mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
- new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
- }
- if (insetsTypesToShow != 0) {
- controlTarget.showInsets(insetsTypesToShow, false);
+ // Show transient bars if they are hidden; restore position if they are visible.
+ mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE);
+ controlTarget.showInsets(restorePositionTypes, false);
+ } else {
+ // Restore visibilities and positions of system bars.
+ controlTarget.showInsets(Type.statusBars() | Type.navigationBars(), false);
}
} else {
boolean sb = mStatusBarController.checkShowTransientBarLw();
@@ -3770,8 +3779,7 @@ public class DisplayPolicy {
// we're no longer on the Keyguard and the screen is ready. We can now request the bars.
mPendingPanicGestureUptime = 0;
if (!isNavBarEmpty(vis)) {
- mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
- new int[] {ITYPE_NAVIGATION_BAR}));
+ mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC);
}
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 133b11116460..c9f463b8fbeb 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -283,8 +283,9 @@ class DragState {
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
mDragWindowHandle.visible = true;
- mDragWindowHandle.canReceiveKeys = false;
- mDragWindowHandle.hasFocus = true;
+ // Allows the system to consume keys when dragging is active. This can also be used to
+ // modify the drag state on key press. Example, cancel drag on escape key.
+ mDragWindowHandle.focusable = true;
mDragWindowHandle.hasWallpaper = false;
mDragWindowHandle.paused = false;
mDragWindowHandle.ownerPid = Process.myPid();
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 3b24584b0357..a79d3bb00907 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -80,8 +80,7 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
mWindowHandle.layoutParamsFlags = 0;
mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
mWindowHandle.visible = true;
- mWindowHandle.canReceiveKeys = false;
- mWindowHandle.hasFocus = false;
+ mWindowHandle.focusable = false;
mWindowHandle.hasWallpaper = false;
mWindowHandle.paused = false;
mWindowHandle.ownerPid = Process.myPid();
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0fe9735c9e46..16c4942ee972 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -227,6 +227,11 @@ final class InputMonitor {
WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name,
InputEventReceiver.Factory inputEventReceiverFactory) {
+ if (!name.contentEquals(INPUT_CONSUMER_NAVIGATION)) {
+ throw new IllegalArgumentException("Illegal input consumer : " + name
+ + ", display: " + mDisplayId);
+ }
+
if (mInputConsumers.containsKey(name)) {
throw new IllegalStateException("Existing input consumer found with name: " + name
+ ", display: " + mDisplayId);
@@ -256,6 +261,11 @@ final class InputMonitor {
// stack, and we need FLAG_NOT_TOUCH_MODAL to ensure other events fall through
consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL;
break;
+ case INPUT_CONSUMER_RECENTS_ANIMATION:
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal input consumer : " + name
+ + ", display: " + mDisplayId);
}
addInputConsumer(name, consumer);
}
@@ -271,8 +281,7 @@ final class InputMonitor {
inputWindowHandle.layoutParamsType = type;
inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis();
inputWindowHandle.visible = isVisible;
- inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
- inputWindowHandle.hasFocus = hasFocus;
+ inputWindowHandle.focusable = hasFocus;
inputWindowHandle.hasWallpaper = hasWallpaper;
inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false;
inputWindowHandle.ownerPid = child.mSession.mPid;
@@ -463,9 +472,6 @@ final class InputMonitor {
mDisplayContent.forAllWindows(this,
true /* traverseTopToBottom */);
- if (mAddWallpaperInputConsumerHandle) {
- mWallpaperInputConsumer.show(mInputTransaction, 0);
- }
if (!mUpdateInputWindowsImmediately) {
mDisplayContent.getPendingTransaction().merge(mInputTransaction);
mDisplayContent.scheduleAnimation();
@@ -572,8 +578,7 @@ final class InputMonitor {
inputWindowHandle.layoutParamsType = type;
inputWindowHandle.dispatchingTimeoutMillis = 0; // it should never receive input
inputWindowHandle.visible = isVisible;
- inputWindowHandle.canReceiveKeys = false;
- inputWindowHandle.hasFocus = false;
+ inputWindowHandle.focusable = false;
inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
inputWindowHandle.scaleFactor = 1;
inputWindowHandle.layoutParamsFlags =
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index 3ffc26a7a8ad..5e7ed3f80e43 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import android.inputmethodservice.InputMethodService;
+import android.view.InsetsState;
import android.view.WindowInsets.Type.InsetsType;
/**
@@ -38,6 +39,13 @@ interface InsetsControlTarget {
}
/**
+ * @return The requested {@link InsetsState} of this target.
+ */
+ default InsetsState getRequestedInsetsState() {
+ return InsetsState.EMPTY;
+ }
+
+ /**
* Instructs the control target to show inset sources.
*
* @param types to specify which types of insets source window should be shown.
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index b7287e718bd6..18a25033b1e6 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -42,7 +42,6 @@ import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.ViewRootImpl;
-import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
@@ -153,15 +152,13 @@ class InsetsPolicy {
return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
}
- @InsetsType int showTransient(IntArray types) {
- @InsetsType int showingTransientTypes = 0;
+ void showTransient(@InternalInsetsType int[] types) {
boolean changed = false;
- for (int i = types.size() - 1; i >= 0; i--) {
- final int type = types.get(i);
+ for (int i = types.length - 1; i >= 0; i--) {
+ final @InternalInsetsType int type = types[i];
if (!isHidden(type)) {
continue;
}
- showingTransientTypes |= InsetsState.toPublicType(type);
if (mShowingTransientTypes.indexOf(type) != -1) {
continue;
}
@@ -189,7 +186,6 @@ class InsetsPolicy {
}
});
}
- return showingTransientTypes;
}
void hideTransient() {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 143fbb0fe48b..6882dc4ca151 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -821,7 +821,7 @@ public class RecentsAnimationController implements DeathRecipient {
: null;
if (targetAppMainWindow != null) {
targetAppMainWindow.getBounds(mTmpRect);
- inputWindowHandle.hasFocus = hasFocus;
+ inputWindowHandle.focusable = hasFocus;
inputWindowHandle.touchableRegion.set(mTmpRect);
return true;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0529abf89f6e..4dbbbc6de32c 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -783,7 +783,7 @@ class Task extends WindowContainer<WindowContainer> {
* taskAppeared callback, and emit a taskRemoved callback when the Task is vanished.
*/
ITaskOrganizer mTaskOrganizer;
- private int mLastTaskOrganizerWindowingMode = -1;
+
/**
* Prevent duplicate calls to onTaskAppeared.
*/
@@ -798,6 +798,7 @@ class Task extends WindowContainer<WindowContainer> {
* organizer for ordering purposes.</li>
* </ul>
*/
+ @VisibleForTesting
boolean mCreatedByOrganizer;
/**
@@ -2788,8 +2789,16 @@ class Task extends WindowContainer<WindowContainer> {
// For calculating screen layout, we need to use the non-decor inset screen area for the
// calculation for compatibility reasons, i.e. screen area without system bars that
// could never go away in Honeycomb.
- final int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
- final int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+ int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
+ int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+ // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
+ // undefined so it can't be used.
+ if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+ compatScreenWidthDp = inOutConfig.screenWidthDp;
+ }
+ if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ compatScreenHeightDp = inOutConfig.screenHeightDp;
+ }
// Reducing the screen layout starting from its parent config.
inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
compatScreenWidthDp, compatScreenHeightDp);
@@ -3820,7 +3829,10 @@ class Task extends WindowContainer<WindowContainer> {
@Override
boolean fillsParent() {
- return matchParentBounds();
+ // From the perspective of policy, we still want to report that this task fills parent
+ // in fullscreen windowing mode even it doesn't match parent bounds because there will be
+ // letterbox around its real content.
+ return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds();
}
@Override
@@ -4820,19 +4832,18 @@ class Task extends WindowContainer<WindowContainer> {
return false;
}
- ITaskOrganizer previousOrganizer = mTaskOrganizer;
+ ITaskOrganizer prevOrganizer = mTaskOrganizer;
// Update the new task organizer before calling sendTaskVanished since it could result in
// a new SurfaceControl getting created that would notify the old organizer about it.
mTaskOrganizer = organizer;
// Let the old organizer know it has lost control.
- sendTaskVanished(previousOrganizer);
+ sendTaskVanished(prevOrganizer);
if (mTaskOrganizer != null) {
sendTaskAppeared();
} else {
// No longer managed by any organizer.
mTaskAppearedSent = false;
- mLastTaskOrganizerWindowingMode = -1;
setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */);
if (mCreatedByOrganizer) {
removeImmediately();
@@ -4853,29 +4864,16 @@ class Task extends WindowContainer<WindowContainer> {
*/
boolean updateTaskOrganizerState(boolean forceUpdate) {
if (!isRootTask()) {
- final boolean result = setTaskOrganizer(null);
- mLastTaskOrganizerWindowingMode = -1;
- return result;
+ return setTaskOrganizer(null);
}
final int windowingMode = getWindowingMode();
- if (!forceUpdate && windowingMode == mLastTaskOrganizerWindowingMode) {
- // If our windowing mode hasn't actually changed, then just stick
- // with our old organizer. This lets us implement the semantic
- // where SysUI can continue to manage it's old tasks
- // while CTS temporarily takes over the registration.
+ final TaskOrganizerController controller = mWmService.mAtmService.mTaskOrganizerController;
+ final ITaskOrganizer organizer = controller.getTaskOrganizer(windowingMode);
+ if (!forceUpdate && mTaskOrganizer == organizer) {
return false;
}
- /*
- * Different windowing modes may be managed by different task organizers. If
- * getTaskOrganizer returns null, we still call setTaskOrganizer to
- * make sure we clear it.
- */
- final ITaskOrganizer org =
- mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
- final boolean result = setTaskOrganizer(org);
- mLastTaskOrganizerWindowingMode = org != null ? windowingMode : -1;
- return result;
+ return setTaskOrganizer(organizer);
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 32511108836e..6550167683a0 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1489,9 +1489,13 @@ final class TaskDisplayArea extends DisplayArea<Task> {
return stack == getTopStack();
}
- boolean isTopNotPinnedStack(Task stack) {
+ boolean isTopNotFinishNotPinnedStack(Task stack) {
for (int i = getStackCount() - 1; i >= 0; --i) {
final Task current = getStackAt(i);
+ final ActivityRecord topAct = current.getTopNonFinishingActivity();
+ if (topAct == null) {
+ continue;
+ }
if (!current.inPinnedWindowingMode()) {
return current == stack;
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 04d134c3649d..f8465ddc02a2 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -20,6 +20,8 @@ import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
@@ -35,7 +37,6 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import android.util.SparseArray;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
@@ -46,9 +47,12 @@ import com.android.internal.util.ArrayUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.Consumer;
@@ -58,7 +62,6 @@ import java.util.function.Consumer;
*/
class TaskOrganizerController extends ITaskOrganizerController.Stub {
private static final String TAG = "TaskOrganizerController";
- private static final LinkedList<IBinder> EMPTY_LIST = new LinkedList<>();
/**
* Masks specifying which configurations are important to report back to an organizer when
@@ -67,6 +70,16 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
private static final int REPORT_CONFIGS = CONTROLLABLE_CONFIGS;
private static final int REPORT_WINDOW_CONFIGS = CONTROLLABLE_WINDOW_CONFIGS;
+ // The set of modes that are currently supports
+ // TODO: Remove once the task organizer can support all modes
+ @VisibleForTesting
+ static final int[] SUPPORTED_WINDOWING_MODES = {
+ WINDOWING_MODE_PINNED,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ WINDOWING_MODE_MULTI_WINDOW,
+ };
+
private final WindowManagerGlobalLock mGlobalLock;
private class DeathRecipient implements IBinder.DeathRecipient {
@@ -233,9 +246,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
void dispose() {
// Move organizer from managing specific windowing modes
- for (int i = mTaskOrganizersForWindowingMode.size() - 1; i >= 0; --i) {
- mTaskOrganizersForWindowingMode.valueAt(i).remove(mOrganizer.getBinder());
- }
+ mTaskOrganizers.remove(mOrganizer.mTaskOrganizer);
// Update tasks currently managed by this organizer to the next one available if
// possible.
@@ -257,8 +268,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
- private final SparseArray<LinkedList<IBinder>> mTaskOrganizersForWindowingMode =
- new SparseArray<>();
+ // List of task organizers by priority
+ private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>();
private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
@@ -285,59 +296,28 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
public void setDeferTaskOrgCallbacksConsumer(Consumer<Runnable> consumer) {
mDeferTaskOrgCallbacksConsumer = consumer;
}
-
/**
- * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
- * If there was already a TaskOrganizer for this windowing mode it will be evicted
- * but will continue to organize it's existing tasks.
+ * Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode.
*/
@Override
- public void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
- if (windowingMode == WINDOWING_MODE_PINNED) {
- if (!mService.mSupportsPictureInPicture) {
- throw new UnsupportedOperationException("Picture in picture is not supported on "
- + "this device");
- }
- } else if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
- if (!mService.mSupportsSplitScreenMultiWindow) {
- throw new UnsupportedOperationException("Split-screen is not supported on this "
- + "device");
- }
- } else if (windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
- if (!mService.mSupportsMultiWindow) {
- throw new UnsupportedOperationException("Multi-window is not supported on this "
- + "device");
- }
- } else {
- throw new UnsupportedOperationException("As of now only Pinned/Split/Multiwindow"
- + " windowing modes are supported for registerTaskOrganizer");
- }
+ public void registerTaskOrganizer(ITaskOrganizer organizer) {
enforceStackPermission("registerTaskOrganizer()");
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- if (getTaskOrganizer(windowingMode) != null) {
- Slog.w(TAG, "Task organizer already exists for windowing mode: "
- + windowingMode);
- }
-
- LinkedList<IBinder> orgs = mTaskOrganizersForWindowingMode.get(windowingMode);
- if (orgs == null) {
- orgs = new LinkedList<>();
- mTaskOrganizersForWindowingMode.put(windowingMode, orgs);
- }
- orgs.add(organizer.asBinder());
- if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
- mTaskOrganizerStates.put(organizer.asBinder(),
- new TaskOrganizerState(organizer, uid));
- }
-
- mService.mRootWindowContainer.forAllTasks((task) -> {
- if (task.getWindowingMode() == windowingMode) {
- task.updateTaskOrganizerState(true /* forceUpdate */);
+ for (int winMode : SUPPORTED_WINDOWING_MODES) {
+ if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
+ mTaskOrganizers.add(organizer);
+ mTaskOrganizerStates.put(organizer.asBinder(),
+ new TaskOrganizerState(organizer, uid));
}
- });
+ mService.mRootWindowContainer.forAllTasks((task) -> {
+ if (task.getWindowingMode() == winMode) {
+ task.updateTaskOrganizerState(true /* forceUpdate */);
+ }
+ });
+ }
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -362,17 +342,22 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
+ /**
+ * @return the task organizer key for a given windowing mode.
+ */
ITaskOrganizer getTaskOrganizer(int windowingMode) {
- final IBinder organizer =
- mTaskOrganizersForWindowingMode.get(windowingMode, EMPTY_LIST).peekLast();
- if (organizer == null) {
- return null;
- }
- final TaskOrganizerState state = mTaskOrganizerStates.get(organizer);
- if (state == null) {
- return null;
+ return isSupportedWindowingMode(windowingMode)
+ ? mTaskOrganizers.peekLast()
+ : null;
+ }
+
+ private boolean isSupportedWindowingMode(int winMode) {
+ for (int i = 0; i < SUPPORTED_WINDOWING_MODES.length; i++) {
+ if (SUPPORTED_WINDOWING_MODES[i] == winMode) {
+ return true;
+ }
}
- return state.mOrganizer.mTaskOrganizer;
+ return false;
}
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
@@ -458,6 +443,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|| mTmpTaskInfo.topActivityType != lastInfo.topActivityType
|| mTmpTaskInfo.isResizeable != lastInfo.isResizeable
|| mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams
+ || mTmpTaskInfo.getConfiguration().windowConfiguration.getWindowingMode()
+ != lastInfo.getConfiguration().windowConfiguration.getWindowingMode()
|| !TaskDescription.equals(mTmpTaskInfo.taskDescription, lastInfo.taskDescription);
if (!changed) {
int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration);
@@ -655,18 +642,19 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
final String innerPrefix = prefix + " ";
pw.print(prefix); pw.println("TaskOrganizerController:");
pw.print(innerPrefix); pw.println("Per windowing mode:");
- for (int i = 0; i < mTaskOrganizersForWindowingMode.size(); i++) {
- final int windowingMode = mTaskOrganizersForWindowingMode.keyAt(i);
- final List<IBinder> taskOrgs = mTaskOrganizersForWindowingMode.valueAt(i);
+ for (int i = 0; i < SUPPORTED_WINDOWING_MODES.length; i++) {
+ final int windowingMode = SUPPORTED_WINDOWING_MODES[i];
pw.println(innerPrefix + " "
+ WindowConfiguration.windowingModeToString(windowingMode) + ":");
- for (int j = 0; j < taskOrgs.size(); j++) {
- final TaskOrganizerState state = mTaskOrganizerStates.get(taskOrgs.get(j));
+ for (final TaskOrganizerState state : mTaskOrganizerStates.values()) {
final ArrayList<Task> tasks = state.mOrganizedTasks;
pw.print(innerPrefix + " ");
pw.println(state.mOrganizer.mTaskOrganizer + " uid=" + state.mUid + ":");
for (int k = 0; k < tasks.size(); k++) {
- pw.println(innerPrefix + " " + tasks.get(k));
+ final Task task = tasks.get(k);
+ if (windowingMode == task.getWindowingMode()) {
+ pw.println(innerPrefix + " " + task);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index a66cd846e8be..f32781a8fcb8 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -242,8 +242,8 @@ class TaskPositioner implements IBinder.DeathRecipient {
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
mDragWindowHandle.visible = true;
- mDragWindowHandle.canReceiveKeys = false;
- mDragWindowHandle.hasFocus = true;
+ // When dragging the window around, we do not want to steal focus for the window.
+ mDragWindowHandle.focusable = false;
mDragWindowHandle.hasWallpaper = false;
mDragWindowHandle.paused = false;
mDragWindowHandle.ownerPid = Process.myPid();
diff --git a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
index 61e9e5082d17..5e81e4008680 100644
--- a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
+++ b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
@@ -24,8 +24,6 @@ import android.annotation.NonNull;
import android.util.ArrayMap;
import android.util.Slog;
-import com.android.server.wm.WindowManagerService.H;
-
import java.io.PrintWriter;
/**
@@ -102,7 +100,13 @@ class UnknownAppVisibilityController {
if (DEBUG_UNKNOWN_APP_VISIBILITY) {
Slog.d(TAG, "App launched activity=" + activity);
}
- mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RESUME);
+ // If the activity was started with launchTaskBehind, the lifecycle will goes to paused
+ // directly, and the process will pass onResume, so we don't need to waiting resume for it.
+ if (!activity.mLaunchTaskBehind) {
+ mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RESUME);
+ } else {
+ mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RELAYOUT);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index cd222a97f4d9..2ce16b2fdd79 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.INPUT_CONSUMER;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
@@ -929,7 +930,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void setShadowRenderer() {
mRenderShadowsInCompositor = Settings.Global.getInt(mContext.getContentResolver(),
- DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0;
+ DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
}
PowerManager mPowerManager;
@@ -5861,6 +5862,11 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void createInputConsumer(IBinder token, String name, int displayId,
InputChannel inputChannel) {
+ if (!mAtmInternal.isCallerRecents(Binder.getCallingUid())
+ && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) {
+ throw new SecurityException("createInputConsumer requires INPUT_CONSUMER permission");
+ }
+
synchronized (mGlobalLock) {
DisplayContent display = mRoot.getDisplayContent(displayId);
if (display != null) {
@@ -5872,6 +5878,11 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public boolean destroyInputConsumer(String name, int displayId) {
+ if (!mAtmInternal.isCallerRecents(Binder.getCallingUid())
+ && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) {
+ throw new SecurityException("destroyInputConsumer requires INPUT_CONSUMER permission");
+ }
+
synchronized (mGlobalLock) {
DisplayContent display = mRoot.getDisplayContent(displayId);
if (display != null) {
@@ -8065,8 +8076,7 @@ public class WindowManagerService extends IWindowManager.Stub
h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags;
h.layoutParamsType = type;
h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- h.canReceiveKeys = false;
- h.hasFocus = false;
+ h.focusable = false;
h.hasWallpaper = false;
h.paused = false;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 49e623d8dd11..0e455d2a5aa6 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -728,7 +728,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* @return The insets state as requested by the client, i.e. the dispatched insets state
* for which the visibilities are overridden with what the client requested.
*/
- InsetsState getRequestedInsetsState() {
+ @Override
+ public InsetsState getRequestedInsetsState() {
return mRequestedInsetsState;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 92177abbbf85..4ff985f6bd43 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -38,6 +38,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
@@ -1327,7 +1328,7 @@ class WindowStateAnimator {
mWin.getDisplayContent().adjustForImeIfNeeded();
}
- return mWin.isAnimating(PARENTS);
+ return mWin.isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6154bef2bda3..22e309cdc2b4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -302,7 +302,6 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -322,7 +321,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -575,13 +573,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private final CertificateMonitor mCertificateMonitor;
private final SecurityLogMonitor mSecurityLogMonitor;
+ private final RemoteBugreportManager mBugreportCollectionManager;
@GuardedBy("getLockObject()")
private NetworkLogger mNetworkLogger;
- private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean();
- private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean();
-
private final SetupContentObserver mSetupContentObserver;
private final DevicePolicyConstantsObserver mConstantsObserver;
@@ -633,44 +629,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@VisibleForTesting
final TransferOwnershipMetadataManager mTransferOwnershipMetadataManager;
- private final Runnable mRemoteBugreportTimeoutRunnable = new Runnable() {
- @Override
- public void run() {
- if(mRemoteBugreportServiceIsActive.get()) {
- onBugreportFailed();
- }
- }
- };
-
- /** Listens only if mHasFeature == true. */
- private final BroadcastReceiver mRemoteBugreportFinishedReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH.equals(intent.getAction())
- && mRemoteBugreportServiceIsActive.get()) {
- onBugreportFinished(intent);
- }
- }
- };
-
- /** Listens only if mHasFeature == true. */
- private final BroadcastReceiver mRemoteBugreportConsentReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- mInjector.getNotificationManager().cancel(LOG_TAG,
- RemoteBugreportUtils.NOTIFICATION_ID);
- if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED.equals(action)) {
- onBugreportSharingAccepted();
- } else if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED.equals(action)) {
- onBugreportSharingDeclined();
- }
- mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
- }
- };
-
public static final class Lifecycle extends SystemService {
private BaseIDevicePolicyManager mService;
@@ -748,17 +706,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
- && userHandle == mOwners.getDeviceOwnerUserId()
- && getDeviceOwnerRemoteBugreportUri() != null) {
- IntentFilter filterConsent = new IntentFilter();
- filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED);
- filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED);
- mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
- mInjector.getNotificationManager().notifyAsUser(LOG_TAG,
- RemoteBugreportUtils.NOTIFICATION_ID,
- RemoteBugreportUtils.buildNotification(mContext,
- DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
- UserHandle.ALL);
+ && userHandle == mOwners.getDeviceOwnerUserId()) {
+ mBugreportCollectionManager.checkForPendingBugreportAfterBoot();
+
}
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
|| ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
@@ -1435,10 +1385,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler);
mDeviceAdminServiceController = new DeviceAdminServiceController(this, mConstants);
-
mOverlayPackagesProvider = new OverlayPackagesProvider(mContext);
-
mTransferOwnershipMetadataManager = mInjector.newTransferOwnershipMetadataManager();
+ mBugreportCollectionManager = new RemoteBugreportManager(this, mInjector);
if (!mHasFeature) {
// Skip the rest of the initialization
@@ -1566,7 +1515,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* Creates a new {@link CallerIdentity} object to represent the caller's identity.
*/
- private CallerIdentity getCallerIdentity(String callerPackage) {
+ private CallerIdentity getCallerIdentity() {
+ final int callerUid = mInjector.binderGetCallingUid();
+ return new CallerIdentity(callerUid, null, null);
+ }
+
+ /**
+ * Creates a new {@link CallerIdentity} object to represent the caller's identity.
+ */
+ private CallerIdentity getCallerIdentity(@NonNull String callerPackage) {
final int callerUid = mInjector.binderGetCallingUid();
if (!isCallingFromPackage(callerPackage, callerUid)) {
@@ -2127,6 +2084,81 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
reqPolicy, /* permission= */ null);
}
+ @NonNull ActiveAdmin getDeviceOwnerOfCallerLocked(final CallerIdentity caller) {
+ ensureLocked();
+ ComponentName doComponent = mOwners.getDeviceOwnerComponent();
+ Preconditions.checkState(doComponent != null,
+ String.format("No device owner for user %d", caller.getUid()));
+
+ // Use the user ID of the caller instead of mOwners.getDeviceOwnerUserId() because
+ // secondary, affiliated users will have their own admin.
+ ActiveAdmin doAdmin = getUserData(caller.getUserId()).mAdminMap.get(doComponent);
+ Preconditions.checkState(doAdmin != null,
+ String.format("Device owner %s for user %d not found", doComponent,
+ caller.getUid()));
+
+ Preconditions.checkSecurity(doAdmin.getUid() == caller.getUid(),
+ String.format("Admin %s is not owned by uid %d, but uid %d", doComponent,
+ caller.getUid(), doAdmin.getUid()));
+
+ Preconditions.checkSecurity(doAdmin.info.getComponent().equals(caller.getComponentName()),
+ String.format("Caller component %s is not device owner",
+ caller.getComponentName()));
+
+ return doAdmin;
+ }
+
+ @NonNull ActiveAdmin getProfileOwnerOfCallerLocked(final CallerIdentity caller) {
+ ensureLocked();
+ final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(caller.getUserId());
+
+ Preconditions.checkState(poAdminComponent != null,
+ String.format("No profile owner for user %d", caller.getUid()));
+
+ ActiveAdmin poAdmin = getUserData(caller.getUserId()).mAdminMap.get(poAdminComponent);
+ Preconditions.checkState(poAdmin != null,
+ String.format("No device profile owner for caller %d", caller.getUid()));
+
+ Preconditions.checkSecurity(poAdmin.getUid() == caller.getUid(),
+ String.format("Admin %s is not owned by uid %d", poAdminComponent,
+ caller.getUid()));
+
+ Preconditions.checkSecurity(poAdmin.info.getComponent().equals(caller.getComponentName()),
+ String.format("Caller component %s is not profile owner",
+ caller.getComponentName()));
+
+ return poAdmin;
+ }
+
+ @NonNull ActiveAdmin getOrganizationOwnedProfileOwnerLocked(final CallerIdentity caller) {
+ final ActiveAdmin profileOwner = getProfileOwnerOfCallerLocked(caller);
+
+ Preconditions.checkSecurity(
+ mOwners.isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()),
+ String.format("Admin %s is not of an org-owned device",
+ profileOwner.info.getComponent()));
+
+ return profileOwner;
+ }
+
+ @NonNull ActiveAdmin getProfileOwnerOrDeviceOwnerLocked(final CallerIdentity caller) {
+ ensureLocked();
+ // Try to find an admin which can use reqPolicy
+ final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(caller.getUserId());
+ final ComponentName doAdminComponent = mOwners.getDeviceOwnerComponent();
+
+ if (poAdminComponent == null && doAdminComponent == null) {
+ throw new IllegalStateException(
+ String.format("No profile or device owner for user %d", caller.getUid()));
+ }
+
+ if (poAdminComponent != null) {
+ return getProfileOwnerOfCallerLocked(caller);
+ }
+
+ return getDeviceOwnerOfCallerLocked(caller);
+ }
+
/**
* Finds an active admin for the caller then checks {@code permission} if admin check failed.
*
@@ -2145,9 +2177,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid);
if (result != null) {
return result;
- } else if (permission != null
- && (mContext.checkCallingPermission(permission)
- == PackageManager.PERMISSION_GRANTED)) {
+ } else if (permission != null && hasCallingPermission(permission)) {
return null;
}
@@ -2844,9 +2874,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle,
Bundle onEnableData) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
DevicePolicyData policy = getUserData(userHandle);
DeviceAdminInfo info = findAdmin(adminReceiver, userHandle,
@@ -2986,7 +3019,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return false;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null;
}
@@ -2997,7 +3034,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return false;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
DevicePolicyData policyData = getUserData(userHandle);
return policyData.mRemovingAdmins.contains(adminReceiver);
@@ -3009,7 +3050,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return false;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity(adminReceiver);
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (administrator == null) {
@@ -3025,8 +3070,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return Collections.EMPTY_LIST;
}
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
- enforceFullCrossUsersPermission(userHandle);
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(userHandle);
final int N = policy.mAdminList.size();
@@ -3046,7 +3094,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return false;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(userHandle);
final int N = policy.mAdminList.size();
@@ -3156,8 +3208,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
enforceUserUnlocked(userHandle);
+
synchronized (getLockObject()) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (admin == null) {
@@ -3309,7 +3365,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return PASSWORD_QUALITY_UNSPECIFIED;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
int mode = PASSWORD_QUALITY_UNSPECIFIED;
@@ -3521,7 +3581,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return 0L;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
long timeout = 0L;
@@ -3547,12 +3611,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean addCrossProfileWidgetProvider(ComponentName admin, String packageName) {
- final int userId = UserHandle.getCallingUserId();
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
List<String> changedProviders = null;
synchronized (getLockObject()) {
- ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin activeAdmin = getProfileOwnerOrDeviceOwnerLocked(identity);
if (activeAdmin.crossProfileWidgetProviders == null) {
activeAdmin.crossProfileWidgetProviders = new ArrayList<>();
}
@@ -3560,7 +3624,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!providers.contains(packageName)) {
providers.add(packageName);
changedProviders = new ArrayList<>(providers);
- saveSettingsLocked(userId);
+ saveSettingsLocked(identity.getUserId());
}
}
@@ -3570,7 +3634,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
if (changedProviders != null) {
- mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders);
+ mLocalService.notifyCrossProfileProvidersChanged(identity.getUserId(),
+ changedProviders);
return true;
}
@@ -3579,12 +3644,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean removeCrossProfileWidgetProvider(ComponentName admin, String packageName) {
- final int userId = UserHandle.getCallingUserId();
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
List<String> changedProviders = null;
synchronized (getLockObject()) {
- ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin activeAdmin = getProfileOwnerOrDeviceOwnerLocked(identity);
if (activeAdmin.crossProfileWidgetProviders == null
|| activeAdmin.crossProfileWidgetProviders.isEmpty()) {
return false;
@@ -3592,7 +3657,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
List<String> providers = activeAdmin.crossProfileWidgetProviders;
if (providers.remove(packageName)) {
changedProviders = new ArrayList<>(providers);
- saveSettingsLocked(userId);
+ saveSettingsLocked(identity.getUserId());
}
}
@@ -3602,7 +3667,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
if (changedProviders != null) {
- mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders);
+ mLocalService.notifyCrossProfileProvidersChanged(identity.getUserId(),
+ changedProviders);
return true;
}
@@ -3611,9 +3677,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public List<String> getCrossProfileWidgetProviders(ComponentName admin) {
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
+
synchronized (getLockObject()) {
- ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin activeAdmin = getProfileOwnerOrDeviceOwnerLocked(identity);
if (activeAdmin.crossProfileWidgetProviders == null
|| activeAdmin.crossProfileWidgetProviders.isEmpty()) {
return null;
@@ -3656,7 +3724,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return 0L;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
return getPasswordExpirationLocked(who, userHandle, parent);
}
@@ -3862,7 +3934,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return 0;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
if (who != null) {
final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
@@ -3902,7 +3978,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
new PasswordMetrics(CREDENTIAL_TYPE_NONE);
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>();
synchronized (getLockObject()) {
List<ActiveAdmin> admins =
@@ -3919,7 +3999,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return true;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
enforceUserUnlocked(userHandle, parent);
synchronized (getLockObject()) {
@@ -3951,7 +4034,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return true;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
enforceManagedProfile(userHandle, "call APIs refering to the parent profile");
synchronized (getLockObject()) {
@@ -3970,7 +4056,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return true;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
enforceNotManagedProfile(userHandle, "check password sufficiency");
enforceUserUnlocked(userHandle);
@@ -4058,12 +4147,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mLockPatternUtils.hasSecureLockScreen()) {
return 0;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
- if (!isCallerWithSystemUid()) {
+ if (!isSystemUid(identity)) {
// This API can be called by an active device admin or by keyguard code.
- if (mContext.checkCallingPermission(permission.ACCESS_KEYGUARD_SECURE_STORAGE)
- != PackageManager.PERMISSION_GRANTED) {
+ if (!hasCallingPermission(permission.ACCESS_KEYGUARD_SECURE_STORAGE)) {
getActiveAdminForCallerLocked(
null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
}
@@ -4106,7 +4198,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return 0;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
ActiveAdmin admin = (who != null)
? getActiveAdminUncheckedLocked(who, userHandle, parent)
@@ -4120,7 +4216,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return UserHandle.USER_NULL;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
ActiveAdmin admin = getAdminWithMinimumFailedPasswordsForWipeLocked(
userHandle, parent);
@@ -4191,8 +4291,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// As of R, only privlleged caller holding RESET_PASSWORD can call resetPassword() to
// set password to an unsecured user.
- if (mContext.checkCallingPermission(permission.RESET_PASSWORD)
- == PackageManager.PERMISSION_GRANTED) {
+ if (hasCallingPermission(permission.RESET_PASSWORD)) {
return setPasswordPrivileged(password, flags, callingUid);
}
@@ -4392,7 +4491,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return 0;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
if (who != null) {
final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
@@ -4465,12 +4568,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
}
+ Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId));
+
if (!mLockPatternUtils.hasSecureLockScreen()) {
// No strong auth timeout on devices not supporting the
// {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature
return 0;
}
- enforceFullCrossUsersPermission(userId);
synchronized (getLockObject()) {
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId, parent);
@@ -4504,8 +4611,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void lockNow(int flags, boolean parent) {
- if (!mHasFeature && mContext.checkCallingPermission(android.Manifest.permission.LOCK_DEVICE)
- != PackageManager.PERMISSION_GRANTED) {
+ if (!mHasFeature && !hasCallingPermission(permission.LOCK_DEVICE)) {
return;
}
@@ -4597,8 +4703,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private void enforceNetworkStackOrProfileOrDeviceOwner(ComponentName who) {
- if (mContext.checkCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
- == PackageManager.PERMISSION_GRANTED) {
+ if (hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)) {
return;
}
enforceProfileOrDeviceOwner(who);
@@ -5677,8 +5782,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
-
- enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId());
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(isSystemUid(identity) || isRootUid(identity)
+ || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL));
final ActiveAdmin admin;
synchronized (getLockObject()) {
@@ -5854,8 +5960,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
if (who == null) {
if ((frpManagementAgentUid != mInjector.binderGetCallingUid())
- && (mContext.checkCallingPermission(permission.MASTER_CLEAR)
- != PackageManager.PERMISSION_GRANTED)) {
+ && !hasCallingPermission(permission.MASTER_CLEAR)) {
throw new SecurityException(
"Must be called by the FRP management agent on device");
}
@@ -5893,9 +5998,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
- enforceFullCrossUsersPermission(userHandle);
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = comp != null
+ ? getCallerIdentity(comp)
+ : getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
synchronized (getLockObject()) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(comp, userHandle);
@@ -5972,13 +6081,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void reportFailedPasswordAttempt(int userHandle) {
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
if (!isSeparateProfileChallengeEnabled(userHandle)) {
enforceNotManagedProfile(userHandle,
"report failed password attempt if separate profile challenge is not in place");
}
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BIND_DEVICE_ADMIN, null);
boolean wipeData = false;
ActiveAdmin strictestAdmin = null;
@@ -6051,9 +6162,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void reportSuccessfulPasswordAttempt(int userHandle) {
- enforceFullCrossUsersPermission(userHandle);
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(userHandle);
@@ -6079,9 +6192,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void reportFailedBiometricAttempt(int userHandle) {
- enforceFullCrossUsersPermission(userHandle);
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
+
if (mInjector.securityLogIsLoggingEnabled()) {
SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0,
/*method strength*/ 0);
@@ -6090,9 +6206,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void reportSuccessfulBiometricAttempt(int userHandle) {
- enforceFullCrossUsersPermission(userHandle);
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
+
if (mInjector.securityLogIsLoggingEnabled()) {
SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1,
/*method strength*/ 0);
@@ -6101,9 +6220,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void reportKeyguardDismissed(int userHandle) {
- enforceFullCrossUsersPermission(userHandle);
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
if (mInjector.securityLogIsLoggingEnabled()) {
SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISSED);
@@ -6112,9 +6233,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void reportKeyguardSecured(int userHandle) {
- enforceFullCrossUsersPermission(userHandle);
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
if (mInjector.securityLogIsLoggingEnabled()) {
SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_SECURED);
@@ -6176,7 +6299,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return null;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
// Scan through active admins and find if anyone has already
@@ -6310,7 +6437,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return false;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = who != null
+ ? getCallerIdentity(who)
+ : getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
// Check for permissions if a particular caller is specified
if (who != null) {
@@ -6340,7 +6473,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
// Ok to return current status.
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = callerPackage != null
+ ? getCallerIdentity(callerPackage)
+ : getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
// It's not critical here, but let's make sure the package name is correct, in case
// we start using it for different purposes.
@@ -6485,24 +6623,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
- final int userHandle = UserHandle.getCallingUserId();
+ final CallerIdentity identity = getCallerIdentity(who);
+
boolean requireAutoTimeChanged = false;
synchronized (getLockObject()) {
- if (isManagedProfile(userHandle)) {
- throw new SecurityException("Managed profile cannot set auto time required");
- }
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ Preconditions.checkSecurity(!isManagedProfile(identity.getUserId()),
+ "Managed profile cannot set auto time required");
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
if (admin.requireAutoTime != required) {
admin.requireAutoTime = required;
- saveSettingsLocked(userHandle);
+ saveSettingsLocked(identity.getUserId());
requireAutoTimeChanged = true;
}
}
// requireAutoTime is now backed by DISALLOW_CONFIG_DATE_TIME restriction, so propagate
// updated restrictions to the framework.
if (requireAutoTimeChanged) {
- pushUserRestrictions(userHandle);
+ pushUserRestrictions(identity.getUserId());
}
// Turn AUTO_TIME on in settings if it is required
if (required) {
@@ -6679,45 +6816,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(isDeviceOwner(identity));
ensureAllUsersAffiliated();
- if (mRemoteBugreportServiceIsActive.get()
- || (getDeviceOwnerRemoteBugreportUri() != null)) {
- Slog.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running.");
- return false;
- }
-
- final long currentTime = System.currentTimeMillis();
- synchronized (getLockObject()) {
- DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
- if (currentTime > policyData.mLastBugReportRequestTime) {
- policyData.mLastBugReportRequestTime = currentTime;
- saveSettingsLocked(UserHandle.USER_SYSTEM);
- }
- }
-
- final long callingIdentity = mInjector.binderClearCallingIdentity();
- try {
- mInjector.getIActivityManager().requestRemoteBugReport();
-
- mRemoteBugreportServiceIsActive.set(true);
- mRemoteBugreportSharingAccepted.set(false);
- registerRemoteBugreportReceivers();
- mInjector.getNotificationManager().notifyAsUser(LOG_TAG,
- RemoteBugreportUtils.NOTIFICATION_ID,
- RemoteBugreportUtils.buildNotification(mContext,
- DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
- mHandler.postDelayed(mRemoteBugreportTimeoutRunnable,
- RemoteBugreportUtils.REMOTE_BUGREPORT_TIMEOUT_MILLIS);
+ if (mBugreportCollectionManager.requestBugreport()) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.REQUEST_BUGREPORT)
.setAdmin(who)
.write();
+
+ final long currentTime = System.currentTimeMillis();
+ synchronized (getLockObject()) {
+ DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
+ if (currentTime > policyData.mLastBugReportRequestTime) {
+ policyData.mLastBugReportRequestTime = currentTime;
+ saveSettingsLocked(UserHandle.USER_SYSTEM);
+ }
+ }
+
return true;
- } catch (RemoteException re) {
- // should never happen
- Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
+ } else {
return false;
- } finally {
- mInjector.binderRestoreCallingIdentity(callingIdentity);
}
}
@@ -6761,146 +6877,36 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
- private String getDeviceOwnerRemoteBugreportUri() {
+ void sendBugreportToDeviceOwner(Uri bugreportUri, String bugreportHash) {
synchronized (getLockObject()) {
- return mOwners.getDeviceOwnerRemoteBugreportUri();
+ final Intent intent = new Intent(DeviceAdminReceiver.ACTION_BUGREPORT_SHARE);
+ intent.setComponent(mOwners.getDeviceOwnerComponent());
+ intent.setDataAndType(bugreportUri, RemoteBugreportManager.BUGREPORT_MIMETYPE);
+ intent.putExtra(DeviceAdminReceiver.EXTRA_BUGREPORT_HASH, bugreportHash);
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ final UriGrantsManagerInternal ugm = LocalServices
+ .getService(UriGrantsManagerInternal.class);
+ final NeededUriGrants needed = ugm.checkGrantUriPermissionFromIntent(intent,
+ Process.SHELL_UID, mOwners.getDeviceOwnerComponent().getPackageName(),
+ mOwners.getDeviceOwnerUserId());
+ ugm.grantUriPermissionUncheckedFromIntent(needed, null);
+
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId()));
}
}
- private void setDeviceOwnerRemoteBugreportUriAndHash(String bugreportUri,
- String bugreportHash) {
+ void setDeviceOwnerRemoteBugreportUriAndHash(String bugreportUri, String bugreportHash) {
synchronized (getLockObject()) {
mOwners.setDeviceOwnerRemoteBugreportUriAndHash(bugreportUri, bugreportHash);
}
}
- private void registerRemoteBugreportReceivers() {
- try {
- IntentFilter filterFinished = new IntentFilter(
- DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH,
- RemoteBugreportUtils.BUGREPORT_MIMETYPE);
- mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
- } catch (IntentFilter.MalformedMimeTypeException e) {
- // should never happen, as setting a constant
- Slog.w(LOG_TAG, "Failed to set type " + RemoteBugreportUtils.BUGREPORT_MIMETYPE, e);
- }
- IntentFilter filterConsent = new IntentFilter();
- filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED);
- filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED);
- mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
- }
-
- private void onBugreportFinished(Intent intent) {
- mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
- mRemoteBugreportServiceIsActive.set(false);
- Uri bugreportUri = intent.getData();
- String bugreportUriString = null;
- if (bugreportUri != null) {
- bugreportUriString = bugreportUri.toString();
- }
- String bugreportHash = intent.getStringExtra(
- DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH);
- if (mRemoteBugreportSharingAccepted.get()) {
- shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
- mInjector.getNotificationManager().cancel(LOG_TAG,
- RemoteBugreportUtils.NOTIFICATION_ID);
- } else {
- setDeviceOwnerRemoteBugreportUriAndHash(bugreportUriString, bugreportHash);
- mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
- RemoteBugreportUtils.buildNotification(mContext,
- DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
- UserHandle.ALL);
- }
- mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
- }
-
- private void onBugreportFailed() {
- mRemoteBugreportServiceIsActive.set(false);
- mInjector.systemPropertiesSet(RemoteBugreportUtils.CTL_STOP,
- RemoteBugreportUtils.REMOTE_BUGREPORT_SERVICE);
- mRemoteBugreportSharingAccepted.set(false);
- setDeviceOwnerRemoteBugreportUriAndHash(null, null);
- mInjector.getNotificationManager().cancel(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID);
- Bundle extras = new Bundle();
- extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON,
- DeviceAdminReceiver.BUGREPORT_FAILURE_FAILED_COMPLETING);
- sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras);
- mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
- mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
- }
-
- private void onBugreportSharingAccepted() {
- mRemoteBugreportSharingAccepted.set(true);
- String bugreportUriString = null;
- String bugreportHash = null;
+ Pair<String, String> getDeviceOwnerRemoteBugreportUriAndHash() {
synchronized (getLockObject()) {
- bugreportUriString = getDeviceOwnerRemoteBugreportUri();
- bugreportHash = mOwners.getDeviceOwnerRemoteBugreportHash();
- }
- if (bugreportUriString != null) {
- shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
- } else if (mRemoteBugreportServiceIsActive.get()) {
- mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
- RemoteBugreportUtils.buildNotification(mContext,
- DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED),
- UserHandle.ALL);
- }
- }
-
- private void onBugreportSharingDeclined() {
- if (mRemoteBugreportServiceIsActive.get()) {
- mInjector.systemPropertiesSet(RemoteBugreportUtils.CTL_STOP,
- RemoteBugreportUtils.REMOTE_BUGREPORT_SERVICE);
- mRemoteBugreportServiceIsActive.set(false);
- mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
- mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
- }
- mRemoteBugreportSharingAccepted.set(false);
- setDeviceOwnerRemoteBugreportUriAndHash(null, null);
- sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_SHARING_DECLINED, null);
- }
-
- private void shareBugreportWithDeviceOwnerIfExists(String bugreportUriString,
- String bugreportHash) {
- ParcelFileDescriptor pfd = null;
- try {
- if (bugreportUriString == null) {
- throw new FileNotFoundException();
- }
- Uri bugreportUri = Uri.parse(bugreportUriString);
- pfd = mContext.getContentResolver().openFileDescriptor(bugreportUri, "r");
-
- synchronized (getLockObject()) {
- Intent intent = new Intent(DeviceAdminReceiver.ACTION_BUGREPORT_SHARE);
- intent.setComponent(mOwners.getDeviceOwnerComponent());
- intent.setDataAndType(bugreportUri, RemoteBugreportUtils.BUGREPORT_MIMETYPE);
- intent.putExtra(DeviceAdminReceiver.EXTRA_BUGREPORT_HASH, bugreportHash);
- intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- final UriGrantsManagerInternal ugm = LocalServices
- .getService(UriGrantsManagerInternal.class);
- final NeededUriGrants needed = ugm.checkGrantUriPermissionFromIntent(intent,
- Process.SHELL_UID, mOwners.getDeviceOwnerComponent().getPackageName(),
- mOwners.getDeviceOwnerUserId());
- ugm.grantUriPermissionUncheckedFromIntent(needed, null);
-
- mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId()));
- }
- } catch (FileNotFoundException e) {
- Bundle extras = new Bundle();
- extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON,
- DeviceAdminReceiver.BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE);
- sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras);
- } finally {
- try {
- if (pfd != null) {
- pfd.close();
- }
- } catch (IOException ex) {
- // Ignore
- }
- mRemoteBugreportSharingAccepted.set(false);
- setDeviceOwnerRemoteBugreportUriAndHash(null, null);
+ final String uri = mOwners.getDeviceOwnerRemoteBugreportUri();
+ return uri == null ? null
+ : new Pair<>(uri, mOwners.getDeviceOwnerRemoteBugreportHash());
}
}
@@ -7033,7 +7039,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return 0;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
final long ident = mInjector.binderClearCallingIdentity();
try {
synchronized (getLockObject()) {
@@ -7229,6 +7239,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return who != null && who.equals(profileOwner);
}
+ /**
+ * Returns {@code true} if the provided caller identity is of a profile owner.
+ * @param identity identity of caller.
+ * @return true if {@code identity} is a profile owner, false otherwise.
+ */
+ public boolean isProfileOwner(CallerIdentity identity) {
+ final ComponentName profileOwner = getProfileOwner(identity.getUserId());
+ return profileOwner != null && profileOwner.equals(identity.getComponentName());
+ }
+
private boolean hasProfileOwner(int userId) {
synchronized (getLockObject()) {
return mOwners.hasProfileOwner(userId);
@@ -7783,7 +7803,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public ComponentName getProfileOwnerAsUser(int userHandle) {
- enforceCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userHandle));
return getProfileOwner(userHandle);
}
@@ -8140,56 +8163,31 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void enforceAcrossUsersPermissions() {
- final int callingUid = mInjector.binderGetCallingUid();
- final int callingPid = mInjector.binderGetCallingPid();
- final String packageName = mContext.getPackageName();
-
- if (isCallerWithSystemUid() || callingUid == Process.ROOT_UID) {
- return;
- }
- if (PermissionChecker.checkPermissionForPreflight(
- mContext, permission.INTERACT_ACROSS_PROFILES, callingPid, callingUid,
- packageName) == PermissionChecker.PERMISSION_GRANTED) {
- return;
- }
- if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS)
- == PackageManager.PERMISSION_GRANTED) {
- return;
- }
- if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS_FULL)
- == PackageManager.PERMISSION_GRANTED) {
- return;
- }
- throw new SecurityException("Calling user does not have INTERACT_ACROSS_PROFILES or"
- + "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL permissions");
+ private boolean hasCallingPermission(String permission) {
+ return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
}
- private void enforceFullCrossUsersPermission(int userHandle) {
- enforceSystemUserOrPermissionIfCrossUser(userHandle,
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ private boolean hasCallingOrSelfPermission(String permission) {
+ return mContext.checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
}
- private void enforceCrossUsersPermission(int userHandle) {
- enforceSystemUserOrPermissionIfCrossUser(userHandle,
- android.Manifest.permission.INTERACT_ACROSS_USERS);
+ private boolean hasPermissionForPreflight(CallerIdentity identity, String permission) {
+ final int callingPid = mInjector.binderGetCallingPid();
+ final String packageName = mContext.getPackageName();
+
+ return PermissionChecker.checkPermissionForPreflight(mContext, permission, callingPid,
+ identity.getUid(), packageName) == PermissionChecker.PERMISSION_GRANTED;
}
- private void enforceSystemUserOrPermission(String permission) {
- if (!(isCallerWithSystemUid() || mInjector.binderGetCallingUid() == Process.ROOT_UID)) {
- mContext.enforceCallingOrSelfPermission(permission,
- "Must be system or have " + permission + " permission");
- }
+ private boolean hasFullCrossUsersPermission(CallerIdentity identity, int userHandle) {
+ return (userHandle == identity.getUserId()) || isSystemUid(identity) || isRootUid(identity)
+ || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL);
}
- private void enforceSystemUserOrPermissionIfCrossUser(int userHandle, String permission) {
- if (userHandle < 0) {
- throw new IllegalArgumentException("Invalid userId " + userHandle);
- }
- if (userHandle == mInjector.userHandleGetCallingUserId()) {
- return;
- }
- enforceSystemUserOrPermission(permission);
+ private boolean hasCrossUsersPermission(CallerIdentity identity, int userHandle) {
+ return (userHandle == identity.getUserId()) || isSystemUid(identity) || isRootUid(identity)
+ || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS);
}
private void enforceManagedProfile(int userId, String message) {
@@ -8249,19 +8247,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
throw new SecurityException("No active admin found");
}
- private void enforceProfileOwnerOrFullCrossUsersPermission(int userId) {
- if (userId == mInjector.userHandleGetCallingUserId()) {
+ private void enforceProfileOwnerOrFullCrossUsersPermission(CallerIdentity identity,
+ int userId) {
+ if (userId == identity.getUserId()) {
synchronized (getLockObject()) {
if (getActiveAdminWithPolicyForUidLocked(null,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid())
- != null) {
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, identity.getUid()) != null) {
// Device Owner/Profile Owner may access the user it runs on.
return;
}
}
}
- // Otherwise, INTERACT_ACROSS_USERS_FULL permission, system UID or root UID is required.
- enforceSystemUserOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId));
}
private boolean canUserUseLockTaskLocked(int userId) {
@@ -8315,6 +8312,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID);
}
+ private boolean isSystemUid(CallerIdentity identity) {
+ return UserHandle.isSameApp(identity.getUid(), Process.SYSTEM_UID);
+ }
+
+ private boolean isRootUid(CallerIdentity identity) {
+ return UserHandle.isSameApp(identity.getUid(), Process.ROOT_UID);
+ }
+
+ private boolean isShellUid(CallerIdentity identity) {
+ return UserHandle.isSameApp(identity.getUid(), Process.SHELL_UID);
+ }
+
protected int getProfileParentId(int userHandle) {
return mInjector.binderWithCleanCallingIdentity(() -> {
UserInfo parentUser = mUserManager.getProfileParent(userHandle);
@@ -8569,7 +8578,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
Objects.requireNonNull(agent, "agent null");
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = admin != null
+ ? getCallerIdentity(admin)
+ : getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
synchronized (getLockObject()) {
final String componentName = agent.flattenToString();
@@ -8769,9 +8783,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
if (packageList != null) {
- int userId = UserHandle.getCallingUserId();
+ int userId = identity.getUserId();
List<AccessibilityServiceInfo> enabledServices = null;
long id = mInjector.binderClearCallingIdentity();
try {
@@ -8801,8 +8816,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
admin.permittedAccessiblityServices = packageList;
saveSettingsLocked(UserHandle.getCallingUserId());
}
@@ -8822,10 +8836,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity) || isProfileOwner(identity));
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return admin.permittedAccessiblityServices;
}
}
@@ -8920,17 +8935,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
- final int callingUserId = mInjector.userHandleGetCallingUserId();
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity) || isProfileOwner(identity));
+
if (packageList != null) {
List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get()
- .getEnabledInputMethodListAsUser(callingUserId);
+ .getEnabledInputMethodListAsUser(identity.getUserId());
if (enabledImes != null) {
List<String> enabledPackages = new ArrayList<String>();
for (InputMethodInfo ime : enabledImes) {
enabledPackages.add(ime.getPackageName());
}
if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList,
- callingUserId)) {
+ identity.getUserId())) {
Slog.e(LOG_TAG, "Cannot set permitted input methods, "
+ "because it contains already enabled input method.");
return false;
@@ -8939,10 +8956,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
admin.permittedInputMethods = packageList;
- saveSettingsLocked(callingUserId);
+ saveSettingsLocked(identity.getUserId());
}
final String[] packageArray =
packageList != null ? ((List<String>) packageList).toArray(new String[0]) : null;
@@ -8960,10 +8976,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity) || isProfileOwner(identity));
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return admin.permittedInputMethods;
}
}
@@ -9037,17 +9054,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
- final int callingUserId = mInjector.userHandleGetCallingUserId();
- if (!isManagedProfile(callingUserId)) {
+ if (!isManagedProfile(identity.getUserId())) {
return false;
}
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
admin.permittedNotificationListeners = packageList;
- saveSettingsLocked(callingUserId);
+ saveSettingsLocked(identity.getUserId());
}
return true;
}
@@ -9058,10 +9074,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ // API contract is to return null if there are no permitted cross-profile notification
+ // listeners, including in Device Owner mode.
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return admin.permittedNotificationListeners;
}
}
@@ -9922,10 +9940,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public String[] getAccountTypesWithManagementDisabledAsUser(int userId, boolean parent) {
- enforceFullCrossUsersPermission(userId);
if (!mHasFeature) {
return null;
}
+ Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId));
+
synchronized (getLockObject()) {
final ArraySet<String> resultSet = new ArraySet<>();
@@ -9992,10 +10014,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userId = UserHandle.getCallingUserId();
synchronized (getLockObject()) {
+ //TODO: This is a silly access control check. Remove.
if (who != null) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final CallerIdentity caller = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDeviceOwner(caller));
}
-
long id = mInjector.binderClearCallingIdentity();
try {
return mIPackageManager.getBlockUninstallForUser(packageName, userId);
@@ -10015,12 +10039,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
+
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
if (admin.disableCallerId != disabled) {
admin.disableCallerId = disabled;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
}
}
DevicePolicyEventLogger
@@ -10036,16 +10062,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
+
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return admin.disableCallerId;
}
}
@Override
public boolean getCrossProfileCallerIdDisabledForUser(int userId) {
- enforceCrossUsersPermission(userId);
+ Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userId));
+
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
return (admin != null) ? admin.disableCallerId : false;
@@ -10058,12 +10090,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
+
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
if (admin.disableContactsSearch != disabled) {
admin.disableContactsSearch = disabled;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
}
}
DevicePolicyEventLogger
@@ -10079,16 +10113,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
+
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return admin.disableContactsSearch;
}
}
@Override
public boolean getCrossProfileContactsSearchDisabledForUser(int userId) {
- enforceCrossUsersPermission(userId);
+ Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userId));
+
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
return (admin != null) ? admin.disableContactsSearch : false;
@@ -10159,12 +10199,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity) || isProfileOwner(identity));
+
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
if (admin.disableBluetoothContactSharing != disabled) {
admin.disableBluetoothContactSharing = disabled;
- saveSettingsLocked(UserHandle.getCallingUserId());
+ saveSettingsLocked(identity.getUserId());
}
}
DevicePolicyEventLogger
@@ -10180,9 +10222,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity) || isProfileOwner(identity));
+
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return admin.disableBluetoothContactSharing;
}
}
@@ -12138,13 +12182,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
- final int userHandle = mInjector.userHandleGetCallingUserId();
- enforceManagedProfile(userHandle, "set organization color");
+ final CallerIdentity identity = getCallerIdentity(who);
+ enforceManagedProfile(identity.getUserId(), "set organization color");
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
admin.organizationColor = color;
- saveSettingsLocked(userHandle);
+ saveSettingsLocked(identity.getUserId());
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_ORGANIZATION_COLOR)
@@ -12157,7 +12200,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
- enforceFullCrossUsersPermission(userId);
+ Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId));
+
enforceManageUsers();
enforceManagedProfile(userId, "set organization color");
synchronized (getLockObject()) {
@@ -12173,10 +12220,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return ActiveAdmin.DEF_ORGANIZATION_COLOR;
}
Objects.requireNonNull(who, "ComponentName is null");
- enforceManagedProfile(mInjector.userHandleGetCallingUserId(), "get organization color");
+ final CallerIdentity identity = getCallerIdentity(who);
+ enforceManagedProfile(identity.getUserId(), "get organization color");
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return admin.organizationColor;
}
}
@@ -12186,7 +12233,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return ActiveAdmin.DEF_ORGANIZATION_COLOR;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
enforceManagedProfile(userHandle, "get organization color");
synchronized (getLockObject()) {
ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
@@ -12202,15 +12253,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
- final int userHandle = mInjector.userHandleGetCallingUserId();
+ final CallerIdentity identity = getCallerIdentity(who);
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
if (!TextUtils.equals(admin.organizationName, text)) {
admin.organizationName = (text == null || text.length() == 0)
? null : text.toString();
- saveSettingsLocked(userHandle);
+ saveSettingsLocked(identity.getUserId());
}
}
}
@@ -12221,10 +12271,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
- enforceManagedProfile(mInjector.userHandleGetCallingUserId(), "get organization name");
+ final CallerIdentity identity = getCallerIdentity(who);
+ enforceManagedProfile(identity.getUserId(), "get organization name");
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return admin.organizationName;
}
}
@@ -12246,7 +12296,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return null;
}
- enforceFullCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
enforceManagedProfile(userHandle, "get organization name");
synchronized (getLockObject()) {
ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
@@ -12260,20 +12314,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public List<String> setMeteredDataDisabledPackages(ComponentName who, List<String> packageNames) {
Objects.requireNonNull(who);
Objects.requireNonNull(packageNames);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkSecurity(isDeviceOwner(identity) || isProfileOwner(identity),
+ String.format("Admin %s does not own the profile", identity.getComponentName()));
if (!mHasFeature) {
return packageNames;
}
synchronized (getLockObject()) {
- final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- final int callingUserId = mInjector.userHandleGetCallingUserId();
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return mInjector.binderWithCleanCallingIdentity(() -> {
- final List<String> excludedPkgs
- = removeInvalidPkgsForMeteredDataRestriction(callingUserId, packageNames);
+ final List<String> excludedPkgs = removeInvalidPkgsForMeteredDataRestriction(
+ identity.getUserId(), packageNames);
admin.meteredDisabledPackages = packageNames;
- pushMeteredDisabledPackagesLocked(callingUserId);
- saveSettingsLocked(callingUserId);
+ pushMeteredDisabledPackagesLocked(identity.getUserId());
+ saveSettingsLocked(identity.getUserId());
return excludedPkgs;
});
}
@@ -12310,9 +12365,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return new ArrayList<>();
}
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkSecurity(isDeviceOwner(identity) || isProfileOwner(identity),
+ String.format("Admin %s does not own the profile", identity.getComponentName()));
+
synchronized (getLockObject()) {
- final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return admin.meteredDisabledPackages == null
? new ArrayList<>() : admin.meteredDisabledPackages;
}
@@ -12337,12 +12395,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
- private boolean hasMarkProfileOwnerOnOrganizationOwnedDevicePermission() {
- return mContext.checkCallingPermission(
- permission.MARK_DEVICE_ORGANIZATION_OWNED)
- == PackageManager.PERMISSION_GRANTED;
- }
-
@Override
public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) {
// As the caller is the system, it must specify the component name of the profile owner
@@ -12355,7 +12407,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Only adb or system apps with the right permission can mark a profile owner on
// organization-owned device.
- if (!(isAdb() || hasMarkProfileOwnerOnOrganizationOwnedDevicePermission())) {
+ if (!(isAdb() || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) {
throw new SecurityException(
"Only the system can mark a profile owner of organization-owned device.");
}
@@ -13483,7 +13535,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public StringParceledListSlice getOwnerInstalledCaCerts(@NonNull UserHandle user) {
final int userId = user.getIdentifier();
- enforceProfileOwnerOrFullCrossUsersPermission(userId);
+ final CallerIdentity identity = getCallerIdentity();
+ enforceProfileOwnerOrFullCrossUsersPermission(identity, userId);
synchronized (getLockObject()) {
return new StringParceledListSlice(
new ArrayList<>(getUserData(userId).mOwnerInstalledCaCerts));
@@ -14100,12 +14153,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
synchronized (getLockObject()) {
- final ActiveAdmin admin = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
admin.mCrossProfileCalendarPackages = packageNames;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_CROSS_PROFILE_CALENDAR_PACKAGES)
@@ -14121,10 +14174,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return Collections.emptyList();
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
synchronized (getLockObject()) {
- final ActiveAdmin admin = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return admin.mCrossProfileCalendarPackages;
}
}
@@ -14136,8 +14189,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userHandle));
- enforceCrossUsersPermission(userHandle);
synchronized (getLockObject()) {
if (mInjector.settingsSecureGetIntForUser(
Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, 0, userHandle) == 0) {
@@ -14159,7 +14215,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return Collections.emptyList();
}
- enforceCrossUsersPermission(userHandle);
+ Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userHandle));
+
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
if (admin != null) {
@@ -14176,16 +14236,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(packageNames, "Package names is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+
final List<String> previousCrossProfilePackages;
synchronized (getLockObject()) {
- final ActiveAdmin admin =
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
previousCrossProfilePackages = admin.mCrossProfilePackages;
if (packageNames.equals(previousCrossProfilePackages)) {
return;
}
admin.mCrossProfilePackages = packageNames;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
}
logSetCrossProfilePackages(who, packageNames);
final CrossProfileApps crossProfileApps = mContext.getSystemService(CrossProfileApps.class);
@@ -14208,10 +14269,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return Collections.emptyList();
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
synchronized (getLockObject()) {
- final ActiveAdmin admin = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
return admin.mCrossProfilePackages;
}
}
@@ -14221,7 +14282,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return Collections.emptyList();
}
- enforceAcrossUsersPermissions();
+ final CallerIdentity identity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isSystemUid(identity) || isRootUid(identity) || hasCallingPermission(
+ permission.INTERACT_ACROSS_USERS) || hasCallingPermission(
+ permission.INTERACT_ACROSS_USERS_FULL) || hasPermissionForPreflight(
+ identity, permission.INTERACT_ACROSS_PROFILES));
synchronized (getLockObject()) {
final List<ActiveAdmin> admins = getProfileOwnerAdminsForCurrentProfileGroup();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
new file mode 100644
index 000000000000..46c9aab5bb97
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
@@ -0,0 +1,324 @@
+/*
+ * 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.devicepolicy;
+
+import static android.app.admin.DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED;
+import static android.app.admin.DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED;
+import static android.app.admin.DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH;
+import static android.app.admin.DevicePolicyManager.EXTRA_BUGREPORT_NOTIFICATION_TYPE;
+import static android.app.admin.DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH;
+import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED;
+import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED;
+import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED;
+
+import android.annotation.IntDef;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.admin.DeviceAdminReceiver;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.format.DateUtils;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
+
+import java.io.FileNotFoundException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Class managing bugreport collection upon device owner's request.
+ */
+public class RemoteBugreportManager {
+ private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
+
+ static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
+
+ private static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS;
+ private static final String CTL_STOP = "ctl.stop";
+ private static final String REMOTE_BUGREPORT_SERVICE = "bugreportd";
+ private static final int NOTIFICATION_ID = SystemMessage.NOTE_REMOTE_BUGREPORT;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ NOTIFICATION_BUGREPORT_STARTED,
+ NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED,
+ NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED
+ })
+ @interface RemoteBugreportNotificationType {}
+ private final DevicePolicyManagerService mService;
+ private final DevicePolicyManagerService.Injector mInjector;
+
+ private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean();
+ private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean();
+ private final Context mContext;
+
+ private final Handler mHandler;
+
+ private final Runnable mRemoteBugreportTimeoutRunnable = () -> {
+ if (mRemoteBugreportServiceIsActive.get()) {
+ onBugreportFailed();
+ }
+ };
+
+ private final BroadcastReceiver mRemoteBugreportFinishedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_REMOTE_BUGREPORT_DISPATCH.equals(intent.getAction())
+ && mRemoteBugreportServiceIsActive.get()) {
+ onBugreportFinished(intent);
+ }
+ }
+ };
+
+ private final BroadcastReceiver mRemoteBugreportConsentReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ mInjector.getNotificationManager().cancel(LOG_TAG, NOTIFICATION_ID);
+ if (ACTION_BUGREPORT_SHARING_ACCEPTED.equals(action)) {
+ onBugreportSharingAccepted();
+ } else if (ACTION_BUGREPORT_SHARING_DECLINED.equals(action)) {
+ onBugreportSharingDeclined();
+ }
+ mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
+ }
+ };
+
+ public RemoteBugreportManager(
+ DevicePolicyManagerService service, DevicePolicyManagerService.Injector injector) {
+ mService = service;
+ mInjector = injector;
+ mContext = service.mContext;
+ mHandler = service.mHandler;
+ }
+
+ private Notification buildNotification(@RemoteBugreportNotificationType int type) {
+ final Intent dialogIntent = new Intent(Settings.ACTION_SHOW_REMOTE_BUGREPORT_DIALOG);
+ dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ dialogIntent.putExtra(EXTRA_BUGREPORT_NOTIFICATION_TYPE, type);
+
+ // Fill the component explicitly to prevent the PendingIntent from being intercepted
+ // and fired with crafted target. b/155183624
+ final ActivityInfo targetInfo = dialogIntent.resolveActivityInfo(
+ mContext.getPackageManager(), PackageManager.MATCH_SYSTEM_ONLY);
+ if (targetInfo != null) {
+ dialogIntent.setComponent(targetInfo.getComponentName());
+ } else {
+ Slog.wtf(LOG_TAG, "Failed to resolve intent for remote bugreport dialog");
+ }
+
+ final PendingIntent pendingDialogIntent = PendingIntent.getActivityAsUser(mContext, type,
+ dialogIntent, 0, null, UserHandle.CURRENT);
+
+ final Notification.Builder builder =
+ new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setOngoing(true)
+ .setLocalOnly(true)
+ .setContentIntent(pendingDialogIntent)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color));
+
+ if (type == NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) {
+ builder.setContentTitle(mContext.getString(
+ R.string.sharing_remote_bugreport_notification_title))
+ .setProgress(0, 0, true);
+ } else if (type == NOTIFICATION_BUGREPORT_STARTED) {
+ builder.setContentTitle(mContext.getString(
+ R.string.taking_remote_bugreport_notification_title))
+ .setProgress(0, 0, true);
+ } else if (type == NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
+ final PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(mContext,
+ NOTIFICATION_ID, new Intent(ACTION_BUGREPORT_SHARING_ACCEPTED),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ final PendingIntent pendingIntentDecline = PendingIntent.getBroadcast(mContext,
+ NOTIFICATION_ID, new Intent(ACTION_BUGREPORT_SHARING_DECLINED),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ builder.addAction(new Notification.Action.Builder(null /* icon */, mContext.getString(
+ R.string.decline_remote_bugreport_action), pendingIntentDecline).build())
+ .addAction(new Notification.Action.Builder(null /* icon */, mContext.getString(
+ R.string.share_remote_bugreport_action), pendingIntentAccept).build())
+ .setContentTitle(mContext.getString(
+ R.string.share_remote_bugreport_notification_title))
+ .setContentText(mContext.getString(
+ R.string.share_remote_bugreport_notification_message_finished))
+ .setStyle(new Notification.BigTextStyle().bigText(mContext.getString(
+ R.string.share_remote_bugreport_notification_message_finished)));
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Initiates bugreport collection.
+ * @return whether collection was initiated successfully.
+ */
+ public boolean requestBugreport() {
+ if (mRemoteBugreportServiceIsActive.get()
+ || (mService.getDeviceOwnerRemoteBugreportUriAndHash() != null)) {
+ Slog.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running.");
+ return false;
+ }
+
+ final long callingIdentity = mInjector.binderClearCallingIdentity();
+ try {
+ mInjector.getIActivityManager().requestRemoteBugReport();
+
+ mRemoteBugreportServiceIsActive.set(true);
+ mRemoteBugreportSharingAccepted.set(false);
+ registerRemoteBugreportReceivers();
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
+ buildNotification(NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
+ mHandler.postDelayed(mRemoteBugreportTimeoutRunnable, REMOTE_BUGREPORT_TIMEOUT_MILLIS);
+ return true;
+ } catch (RemoteException re) {
+ // should never happen
+ Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
+ return false;
+ } finally {
+ mInjector.binderRestoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ private void registerRemoteBugreportReceivers() {
+ try {
+ final IntentFilter filterFinished =
+ new IntentFilter(ACTION_REMOTE_BUGREPORT_DISPATCH, BUGREPORT_MIMETYPE);
+ mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ // should never happen, as setting a constant
+ Slog.w(LOG_TAG, "Failed to set type " + BUGREPORT_MIMETYPE, e);
+ }
+ final IntentFilter filterConsent = new IntentFilter();
+ filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
+ filterConsent.addAction(ACTION_BUGREPORT_SHARING_ACCEPTED);
+ mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
+ }
+
+ private void onBugreportFinished(Intent intent) {
+ mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
+ mRemoteBugreportServiceIsActive.set(false);
+ final Uri bugreportUri = intent.getData();
+ String bugreportUriString = null;
+ if (bugreportUri != null) {
+ bugreportUriString = bugreportUri.toString();
+ }
+ final String bugreportHash = intent.getStringExtra(EXTRA_REMOTE_BUGREPORT_HASH);
+ if (mRemoteBugreportSharingAccepted.get()) {
+ shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
+ mInjector.getNotificationManager().cancel(LOG_TAG,
+ NOTIFICATION_ID);
+ } else {
+ mService.setDeviceOwnerRemoteBugreportUriAndHash(bugreportUriString, bugreportHash);
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
+ buildNotification(NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
+ UserHandle.ALL);
+ }
+ mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
+ }
+
+ private void onBugreportFailed() {
+ mRemoteBugreportServiceIsActive.set(false);
+ mInjector.systemPropertiesSet(CTL_STOP, REMOTE_BUGREPORT_SERVICE);
+ mRemoteBugreportSharingAccepted.set(false);
+ mService.setDeviceOwnerRemoteBugreportUriAndHash(null, null);
+ mInjector.getNotificationManager().cancel(LOG_TAG, NOTIFICATION_ID);
+ final Bundle extras = new Bundle();
+ extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON,
+ DeviceAdminReceiver.BUGREPORT_FAILURE_FAILED_COMPLETING);
+ mService.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras);
+ mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
+ mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
+ }
+
+ private void onBugreportSharingAccepted() {
+ mRemoteBugreportSharingAccepted.set(true);
+ final Pair<String, String> uriAndHash = mService.getDeviceOwnerRemoteBugreportUriAndHash();
+ if (uriAndHash != null) {
+ shareBugreportWithDeviceOwnerIfExists(uriAndHash.first, uriAndHash.second);
+ } else if (mRemoteBugreportServiceIsActive.get()) {
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
+ buildNotification(NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED),
+ UserHandle.ALL);
+ }
+ }
+
+ private void onBugreportSharingDeclined() {
+ if (mRemoteBugreportServiceIsActive.get()) {
+ mInjector.systemPropertiesSet(CTL_STOP,
+ REMOTE_BUGREPORT_SERVICE);
+ mRemoteBugreportServiceIsActive.set(false);
+ mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
+ mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
+ }
+ mRemoteBugreportSharingAccepted.set(false);
+ mService.setDeviceOwnerRemoteBugreportUriAndHash(null, null);
+ mService.sendDeviceOwnerCommand(
+ DeviceAdminReceiver.ACTION_BUGREPORT_SHARING_DECLINED, null);
+ }
+
+ private void shareBugreportWithDeviceOwnerIfExists(
+ String bugreportUriString, String bugreportHash) {
+ try {
+ if (bugreportUriString == null) {
+ throw new FileNotFoundException();
+ }
+ final Uri bugreportUri = Uri.parse(bugreportUriString);
+ mService.sendBugreportToDeviceOwner(bugreportUri, bugreportHash);
+ } catch (FileNotFoundException e) {
+ final Bundle extras = new Bundle();
+ extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON,
+ DeviceAdminReceiver.BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE);
+ mService.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras);
+ } finally {
+ mRemoteBugreportSharingAccepted.set(false);
+ mService.setDeviceOwnerRemoteBugreportUriAndHash(null, null);
+ }
+ }
+
+ /**
+ * Check if a bugreport was collected but not shared before reboot because the user didn't act
+ * upon sharing notification.
+ */
+ public void checkForPendingBugreportAfterBoot() {
+ if (mService.getDeviceOwnerRemoteBugreportUriAndHash() == null) {
+ return;
+ }
+ final IntentFilter filterConsent = new IntentFilter();
+ filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
+ filterConsent.addAction(ACTION_BUGREPORT_SHARING_ACCEPTED);
+ mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
+ buildNotification(NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED), UserHandle.ALL);
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
deleted file mode 100644
index 1630f271a296..000000000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2016 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.devicepolicy;
-
-import android.annotation.IntDef;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.format.DateUtils;
-import android.util.Slog;
-
-import com.android.internal.R;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Utilities class for the remote bugreport operation.
- */
-class RemoteBugreportUtils {
-
- private static final String TAG = "RemoteBugreportUtils";
- static final int NOTIFICATION_ID = SystemMessage.NOTE_REMOTE_BUGREPORT;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED,
- DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED,
- DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED
- })
- @interface RemoteBugreportNotificationType {}
-
- static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS;
-
- static final String CTL_STOP = "ctl.stop";
- static final String REMOTE_BUGREPORT_SERVICE = "bugreportd";
-
- static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
-
- static Notification buildNotification(Context context,
- @RemoteBugreportNotificationType int type) {
- Intent dialogIntent = new Intent(Settings.ACTION_SHOW_REMOTE_BUGREPORT_DIALOG);
- dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- dialogIntent.putExtra(DevicePolicyManager.EXTRA_BUGREPORT_NOTIFICATION_TYPE, type);
-
- // Fill the component explicitly to prevent the PendingIntent from being intercepted
- // and fired with crafted target. b/155183624
- ActivityInfo targetInfo = dialogIntent.resolveActivityInfo(
- context.getPackageManager(), PackageManager.MATCH_SYSTEM_ONLY);
- if (targetInfo != null) {
- dialogIntent.setComponent(targetInfo.getComponentName());
- } else {
- Slog.wtf(TAG, "Failed to resolve intent for remote bugreport dialog");
- }
-
- PendingIntent pendingDialogIntent = PendingIntent.getActivityAsUser(context, type,
- dialogIntent, 0, null, UserHandle.CURRENT);
-
- Notification.Builder builder =
- new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
- .setOngoing(true)
- .setLocalOnly(true)
- .setContentIntent(pendingDialogIntent)
- .setColor(context.getColor(
- com.android.internal.R.color.system_notification_accent_color));
-
- if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) {
- builder.setContentTitle(context.getString(
- R.string.sharing_remote_bugreport_notification_title))
- .setProgress(0, 0, true);
- } else if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED) {
- builder.setContentTitle(context.getString(
- R.string.taking_remote_bugreport_notification_title))
- .setProgress(0, 0, true);
- } else if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
- PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(context, NOTIFICATION_ID,
- new Intent(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED),
- PendingIntent.FLAG_CANCEL_CURRENT);
- PendingIntent pendingIntentDecline = PendingIntent.getBroadcast(context,
- NOTIFICATION_ID, new Intent(
- DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED),
- PendingIntent.FLAG_CANCEL_CURRENT);
- builder.addAction(new Notification.Action.Builder(null /* icon */, context.getString(
- R.string.decline_remote_bugreport_action), pendingIntentDecline).build())
- .addAction(new Notification.Action.Builder(null /* icon */, context.getString(
- R.string.share_remote_bugreport_action), pendingIntentAccept).build())
- .setContentTitle(context.getString(
- R.string.share_remote_bugreport_notification_title))
- .setContentText(context.getString(
- R.string.share_remote_bugreport_notification_message_finished))
- .setStyle(new Notification.BigTextStyle().bigText(context.getString(
- R.string.share_remote_bugreport_notification_message_finished)));
- }
-
- return builder.build();
- }
-}
-
diff --git a/services/tests/servicestests/src/android/location/timezone/LocationTimeZoneEventTest.java b/services/tests/servicestests/src/android/location/timezone/LocationTimeZoneEventTest.java
index 80373ac66109..f9dd7dc86ad5 100644
--- a/services/tests/servicestests/src/android/location/timezone/LocationTimeZoneEventTest.java
+++ b/services/tests/servicestests/src/android/location/timezone/LocationTimeZoneEventTest.java
@@ -23,6 +23,8 @@ import static org.junit.Assert.assertNotEquals;
import static java.util.Collections.singletonList;
+import android.os.UserHandle;
+
import org.junit.Test;
import java.util.List;
@@ -33,6 +35,10 @@ public class LocationTimeZoneEventTest {
private static final List<String> ARBITRARY_TIME_ZONE_IDS = singletonList("Europe/London");
+ private static final UserHandle ARBITRARY_USER_HANDLE = UserHandle.SYSTEM;
+ private static final UserHandle ARBITRARY_USER_HANDLE2 =
+ UserHandle.of(ARBITRARY_USER_HANDLE.getIdentifier() + 1);
+
@Test(expected = RuntimeException.class)
public void testSetInvalidEventType() {
new LocationTimeZoneEvent.Builder().setEventType(Integer.MAX_VALUE);
@@ -41,6 +47,7 @@ public class LocationTimeZoneEventTest {
@Test(expected = RuntimeException.class)
public void testBuildUnsetEventType() {
new LocationTimeZoneEvent.Builder()
+ .setUserHandle(ARBITRARY_USER_HANDLE)
.setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS)
.setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS)
.build();
@@ -49,6 +56,7 @@ public class LocationTimeZoneEventTest {
@Test(expected = RuntimeException.class)
public void testInvalidTimeZoneIds() {
new LocationTimeZoneEvent.Builder()
+ .setUserHandle(ARBITRARY_USER_HANDLE)
.setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
.setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS)
.setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS)
@@ -58,6 +66,7 @@ public class LocationTimeZoneEventTest {
@Test
public void testEquals() {
LocationTimeZoneEvent.Builder builder1 = new LocationTimeZoneEvent.Builder()
+ .setUserHandle(ARBITRARY_USER_HANDLE)
.setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
.setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS);
{
@@ -66,6 +75,7 @@ public class LocationTimeZoneEventTest {
}
LocationTimeZoneEvent.Builder builder2 = new LocationTimeZoneEvent.Builder()
+ .setUserHandle(ARBITRARY_USER_HANDLE)
.setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
.setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS);
{
@@ -75,6 +85,22 @@ public class LocationTimeZoneEventTest {
assertEquals(two, one);
}
+ builder1.setUserHandle(ARBITRARY_USER_HANDLE2);
+ {
+ LocationTimeZoneEvent one = builder1.build();
+ LocationTimeZoneEvent two = builder2.build();
+ assertNotEquals(one, two);
+ assertNotEquals(two, one);
+ }
+
+ builder2.setUserHandle(ARBITRARY_USER_HANDLE2);
+ {
+ LocationTimeZoneEvent one = builder1.build();
+ LocationTimeZoneEvent two = builder2.build();
+ assertEquals(one, two);
+ assertEquals(two, one);
+ }
+
builder1.setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS + 1);
{
LocationTimeZoneEvent one = builder1.build();
@@ -127,6 +153,7 @@ public class LocationTimeZoneEventTest {
@Test
public void testParcelable() {
LocationTimeZoneEvent.Builder builder = new LocationTimeZoneEvent.Builder()
+ .setUserHandle(ARBITRARY_USER_HANDLE)
.setEventType(LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE)
.setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS);
assertRoundTripParcelable(builder.build());
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 32afe8244eb6..e6fc792c6a9d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4465,6 +4465,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+ mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
// Even if the caller is the managed profile, the current user is the user 0
when(getServices().iactivityManager.getCurrentUser())
@@ -5694,6 +5695,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
final long ident = mServiceContext.binder.clearCallingIdentity();
configureContextForAccess(mServiceContext, true);
+ mServiceContext.permissions.add(permission.MARK_DEVICE_ORGANIZATION_OWNED);
mServiceContext.binder.callingUid =
UserHandle.getUid(CALLER_USER_HANDLE,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index ce7ac9e796d2..09b6d7b0cd7e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -259,18 +259,7 @@ public class DpmMockContext extends MockContext {
@Override
public int checkPermission(String permission, int pid, int uid) {
- if (UserHandle.isSameApp(binder.getCallingUid(), SYSTEM_UID)) {
- return PackageManager.PERMISSION_GRANTED; // Assume system has all permissions.
- }
- List<String> permissions = binder.callingPermissions.get(binder.getCallingUid());
- if (permissions == null) {
- permissions = callerPermissions;
- }
- if (permissions.contains(permission)) {
- return PackageManager.PERMISSION_GRANTED;
- } else {
- return PackageManager.PERMISSION_DENIED;
- }
+ return checkPermission(permission);
}
@Override
@@ -480,11 +469,32 @@ public class DpmMockContext extends MockContext {
@Override
public int checkCallingPermission(String permission) {
- return spiedContext.checkCallingPermission(permission);
+ return checkPermission(permission);
+ }
+
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ return checkPermission(permission);
}
@Override
public void startActivityAsUser(Intent intent, UserHandle userHandle) {
spiedContext.startActivityAsUser(intent, userHandle);
}
+
+ private int checkPermission(String permission) {
+ if (UserHandle.isSameApp(binder.getCallingUid(), SYSTEM_UID)) {
+ return PackageManager.PERMISSION_GRANTED; // Assume system has all permissions.
+ }
+ List<String> permissions = binder.callingPermissions.get(binder.getCallingUid());
+ if (permissions == null) {
+ permissions = callerPermissions;
+ }
+ if (permissions.contains(permission)) {
+ return PackageManager.PERMISSION_GRANTED;
+ } else {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java b/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
index 0219f2201675..4c3674767e97 100644
--- a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
@@ -15,13 +15,15 @@
*/
package com.android.server.job;
-import android.util.KeyValueListParser;
+import android.annotation.Nullable;
+import android.provider.DeviceConfig;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.job.JobSchedulerService.MaxJobCounts;
+import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,19 +31,32 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MaxJobCountsTest {
+ @After
+ public void tearDown() throws Exception {
+ resetConfig();
+ }
+
+ private void resetConfig() {
+ // DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "total", "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "maxbg", "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "minbg", "", false);
+ }
- private void check(String config,
+ private void check(@Nullable DeviceConfig.Properties config,
int defaultTotal, int defaultMaxBg, int defaultMinBg,
- int expectedTotal, int expectedMaxBg, int expectedMinBg) {
- final KeyValueListParser parser = new KeyValueListParser(',');
- parser.setString(config);
+ int expectedTotal, int expectedMaxBg, int expectedMinBg) throws Exception {
+ resetConfig();
+ if (config != null) {
+ DeviceConfig.setProperties(config);
+ }
final MaxJobCounts counts = new JobSchedulerService.MaxJobCounts(
defaultTotal, "total",
defaultMaxBg, "maxbg",
defaultMinBg, "minbg");
- counts.parse(parser);
+ counts.update();
Assert.assertEquals(expectedTotal, counts.getMaxTotal());
Assert.assertEquals(expectedMaxBg, counts.getMaxBg());
@@ -49,24 +64,35 @@ public class MaxJobCountsTest {
}
@Test
- public void test() {
+ public void test() throws Exception {
// Tests with various combinations.
- check("", /*default*/ 5, 1, 0, /*expected*/ 5, 1, 0);
- check("", /*default*/ 5, 0, 0, /*expected*/ 5, 1, 0);
- check("", /*default*/ 0, 0, 0, /*expected*/ 1, 1, 0);
- check("", /*default*/ -1, -1, -1, /*expected*/ 1, 1, 0);
- check("", /*default*/ 5, 5, 5, /*expected*/ 5, 5, 4);
- check("", /*default*/ 6, 5, 6, /*expected*/ 6, 5, 5);
- check("", /*default*/ 4, 5, 6, /*expected*/ 4, 4, 3);
- check("", /*default*/ 5, 1, 1, /*expected*/ 5, 1, 1);
- check("", /*default*/ 15, 15, 15, /*expected*/ 15, 15, 14);
- check("", /*default*/ 16, 16, 16, /*expected*/ 16, 16, 15);
- check("", /*default*/ 20, 20, 20, /*expected*/ 16, 16, 15);
+ check(null, /*default*/ 5, 1, 0, /*expected*/ 5, 1, 0);
+ check(null, /*default*/ 5, 0, 0, /*expected*/ 5, 1, 0);
+ check(null, /*default*/ 0, 0, 0, /*expected*/ 1, 1, 0);
+ check(null, /*default*/ -1, -1, -1, /*expected*/ 1, 1, 0);
+ check(null, /*default*/ 5, 5, 5, /*expected*/ 5, 5, 4);
+ check(null, /*default*/ 6, 5, 6, /*expected*/ 6, 5, 5);
+ check(null, /*default*/ 4, 5, 6, /*expected*/ 4, 4, 3);
+ check(null, /*default*/ 5, 1, 1, /*expected*/ 5, 1, 1);
+ check(null, /*default*/ 15, 15, 15, /*expected*/ 15, 15, 14);
+ check(null, /*default*/ 16, 16, 16, /*expected*/ 16, 16, 15);
+ check(null, /*default*/ 20, 20, 20, /*expected*/ 16, 16, 15);
// Test for overriding with a setting string.
- check("total=5,maxbg=4,minbg=3", /*default*/ 9, 9, 9, /*expected*/ 5, 4, 3);
- check("total=5", /*default*/ 9, 9, 9, /*expected*/ 5, 5, 4);
- check("maxbg=4", /*default*/ 9, 9, 9, /*expected*/ 9, 4, 4);
- check("minbg=3", /*default*/ 9, 9, 9, /*expected*/ 9, 9, 3);
+ check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+ .setInt("total", 5)
+ .setInt("maxbg", 4)
+ .setInt("minbg", 3)
+ .build(),
+ /*default*/ 9, 9, 9, /*expected*/ 5, 4, 3);
+ check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+ .setInt("total", 5).build(),
+ /*default*/ 9, 9, 9, /*expected*/ 5, 5, 4);
+ check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+ .setInt("maxbg", 4).build(),
+ /*default*/ 9, 9, 9, /*expected*/ 9, 4, 4);
+ check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+ .setInt("minbg", 3).build(),
+ /*default*/ 9, 9, 9, /*expected*/ 9, 9, 3);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
index 946f27e09fdb..d36dcce800eb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
@@ -20,7 +20,7 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageParser
-import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.Postsubmit
import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.appInfo
import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.pkgInfo
import com.android.server.pm.parsing.pkg.AndroidPackage
@@ -38,7 +38,7 @@ import org.junit.runners.Parameterized
* This test has to be updated manually whenever the info generation behavior changes, since
* there's no single place where flag -> field is defined besides this test.
*/
-@Presubmit
+@Postsubmit
@RunWith(Parameterized::class)
class AndroidPackageInfoFlagBehaviorTest : AndroidPackageParsingTestBase() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
index f96ebda67602..574921cdbd05 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
@@ -17,26 +17,20 @@
package com.android.server.pm.parsing
import android.content.pm.PackageManager
-import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.Postsubmit
import androidx.test.filters.LargeTest
import com.google.common.truth.Expect
-
import org.junit.Rule
import org.junit.Test
-import org.junit.rules.Timeout
-import java.util.concurrent.TimeUnit
/**
* Collects APKs from the device and verifies that the new parsing behavior outputs
* the same exposed Info object as the old parsing logic.
*/
-@Presubmit
+@Postsubmit
class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
@get:Rule
- val timeout = Timeout(4, TimeUnit.MINUTES)
-
- @get:Rule
val expect = Expect.create()
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index f860e174fd15..708d802a7533 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -544,7 +544,7 @@ public class ActivityRecordTests extends WindowTestsBase {
mActivity = new ActivityBuilder(mAtm)
.setTask(mTask)
.setLaunchTaskBehind(true)
- .setConfigChanges(CONFIG_ORIENTATION)
+ .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
.build();
mActivity.setState(Task.ActivityState.STOPPED, "Testing");
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 555906d4c910..608305c33168 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -43,7 +43,6 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import android.platform.test.annotations.Presubmit;
-import android.util.IntArray;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.test.InsetsModeSession;
@@ -242,8 +241,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
}).when(policy).startAnimation(anyBoolean(), any(), any());
policy.updateBarControlTarget(mAppWindow);
- policy.showTransient(
- IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
waitUntilWindowAnimatorIdle();
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -271,8 +269,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
policy.updateBarControlTarget(mAppWindow);
- policy.showTransient(
- IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
waitUntilWindowAnimatorIdle();
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -301,8 +298,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
policy.updateBarControlTarget(mAppWindow);
- policy.showTransient(
- IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
waitUntilWindowAnimatorIdle();
InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -340,8 +336,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
policy.updateBarControlTarget(app);
- policy.showTransient(
- IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
policy.updateBarControlTarget(app2);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 085230d35c6a..59f8cc8c3412 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -346,8 +346,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
assertTrue(rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
provider.getSource().setVisible(false);
- mDisplayContent.getInsetsPolicy().showTransient(
- IntArray.wrap(new int[] { ITYPE_STATUS_BAR }));
+ mDisplayContent.getInsetsPolicy().showTransient(new int[] { ITYPE_STATUS_BAR });
assertTrue(mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR));
assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index fc54e1de888f..08537a4ea9c1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -50,6 +50,7 @@ import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -245,17 +246,17 @@ public class TaskRecordTests extends WindowTestsBase {
final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
final DisplayContent display = new TestDisplayContent.Builder(mAtm,
fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
- assertTrue(mRootWindowContainer.getDisplayContent(display.mDisplayId) != null);
+ assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
// Fix the display orientation to landscape which is the natural rotation (0) for the test
// display.
final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
- Task stack = new StackBuilder(mRootWindowContainer)
+ final Task stack = new StackBuilder(mRootWindowContainer)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
- Task task = stack.getBottomMostTask();
- ActivityRecord root = task.getTopNonFinishingActivity();
+ final Task task = stack.getBottomMostTask();
+ final ActivityRecord root = task.getTopNonFinishingActivity();
assertEquals(fullScreenBounds, task.getBounds());
@@ -267,7 +268,7 @@ public class TaskRecordTests extends WindowTestsBase {
assertEquals(fullScreenBounds.height(), task.getBounds().height());
// Top activity gets used
- ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setStack(stack).build();
+ final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setStack(stack).build();
assertEquals(top, task.getTopNonFinishingActivity());
top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
@@ -304,6 +305,33 @@ public class TaskRecordTests extends WindowTestsBase {
}
@Test
+ public void testReportsOrientationRequestInLetterboxForOrientation() {
+ final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+ final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm,
+ fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
+ assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
+ // Fix the display orientation to landscape which is the natural rotation (0) for the test
+ // display.
+ final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
+ dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+
+ final Task stack = new StackBuilder(mRootWindowContainer)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+ final Task task = stack.getBottomMostTask();
+ ActivityRecord root = task.getTopNonFinishingActivity();
+
+ assertEquals(fullScreenBounds, task.getBounds());
+
+ // Setting app to fixed portrait fits within parent
+ root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ assertThat(task.getBounds().width()).isLessThan(task.getBounds().height());
+
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation());
+ }
+
+ @Test
public void testIgnoresForcedOrientationWhenParentHandles() {
final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
DisplayContent display = new TestDisplayContent.Builder(
@@ -404,6 +432,31 @@ public class TaskRecordTests extends WindowTestsBase {
}
@Test
+ public void testComputeConfigResourceLayoutOverrides() {
+ final Rect fullScreenBounds = new Rect(0, 0, 1000, 2500);
+ TestDisplayContent display = new TestDisplayContent.Builder(
+ mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+ final Configuration inOutConfig = new Configuration();
+ final Configuration parentConfig = new Configuration();
+ final Rect nonLongBounds = new Rect(0, 0, 1000, 1250);
+ parentConfig.windowConfiguration.setBounds(fullScreenBounds);
+ parentConfig.windowConfiguration.setAppBounds(fullScreenBounds);
+ parentConfig.densityDpi = 400;
+ parentConfig.screenHeightDp = (fullScreenBounds.bottom * 160) / parentConfig.densityDpi;
+ parentConfig.screenWidthDp = (fullScreenBounds.right * 160) / parentConfig.densityDpi;
+ parentConfig.windowConfiguration.setRotation(ROTATION_0);
+
+ // Set BOTH screenW/H to an override value
+ inOutConfig.screenWidthDp = nonLongBounds.width() * 160 / parentConfig.densityDpi;
+ inOutConfig.screenHeightDp = nonLongBounds.height() * 160 / parentConfig.densityDpi;
+ task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+
+ // screenLayout should honor override when both screenW/H are set.
+ assertTrue((inOutConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_NO) != 0);
+ }
+
+ @Test
public void testComputeNestedConfigResourceOverrides() {
final Task task = new TaskBuilder(mSupervisor).build();
assertTrue(task.getResolvedOverrideBounds().isEmpty());
diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 6ed762283524..78dfd407ff4e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -55,6 +55,18 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase {
}
@Test
+ public void testSkipResume() {
+ final ActivityRecord activity = createTestActivityRecord(mDisplayContent);
+ activity.mLaunchTaskBehind = true;
+ mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
+ mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(activity);
+
+ // Make sure our handler processed the message.
+ waitHandlerIdle(mWm.mH);
+ assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
+ }
+
+ @Test
public void testMultiple() {
final ActivityRecord activity1 = createTestActivityRecord(mDisplayContent);
final ActivityRecord activity2 = createTestActivityRecord(mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 91352972e772..289d54e967f5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -88,20 +88,14 @@ import java.util.List;
@Presubmit
@RunWith(WindowTestRunner.class)
public class WindowOrganizerTests extends WindowTestsBase {
- private ITaskOrganizer registerMockOrganizer(int windowingMode) {
+ private ITaskOrganizer registerMockOrganizer() {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
when(organizer.asBinder()).thenReturn(new Binder());
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
- organizer, windowingMode);
-
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer);
return organizer;
}
- private ITaskOrganizer registerMockOrganizer() {
- return registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
- }
-
Task createTask(Task stack, boolean fakeDraw) {
final Task task = createTaskInStack(stack, 0);
@@ -133,11 +127,12 @@ public class WindowOrganizerTests extends WindowTestsBase {
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer();
- task.setTaskOrganizer(organizer);
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ stack.setTaskOrganizer(organizer);
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- task.removeImmediately();
+ stack.removeImmediately();
verify(organizer).onTaskVanished(any());
}
@@ -147,16 +142,17 @@ public class WindowOrganizerTests extends WindowTestsBase {
final Task task = createTask(stack, false);
final ITaskOrganizer organizer = registerMockOrganizer();
- task.setTaskOrganizer(organizer);
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ stack.setTaskOrganizer(organizer);
verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- task.setHasBeenVisible(true);
+ stack.setHasBeenVisible(true);
assertTrue(stack.getHasBeenVisible());
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- task.removeImmediately();
+ stack.removeImmediately();
verify(organizer).onTaskVanished(any());
}
@@ -169,42 +165,14 @@ public class WindowOrganizerTests extends WindowTestsBase {
// In this test we skip making the Task visible, and verify
// that even though a TaskOrganizer is set remove doesn't emit
// a vanish callback, because we never emitted appear.
- task.setTaskOrganizer(organizer);
+ stack.setTaskOrganizer(organizer);
verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- task.removeImmediately();
+ stack.removeImmediately();
verify(organizer, never()).onTaskVanished(any());
}
@Test
- public void testSwapOrganizer() throws RemoteException {
- final Task stack = createStack();
- final Task task = createTask(stack);
- final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
- final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
-
- task.setTaskOrganizer(organizer);
- verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- task.setTaskOrganizer(organizer2);
- verify(organizer).onTaskVanished(any());
- verify(organizer2).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- }
-
- @Test
- public void testSwapWindowingModes() throws RemoteException {
- final Task stack = createStack();
- final Task task = createTask(stack);
- final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
- final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
-
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- stack.setWindowingMode(WINDOWING_MODE_PINNED);
- verify(organizer).onTaskVanished(any());
- verify(organizer2).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- }
-
- @Test
public void testTaskNoDraw() throws RemoteException {
final Task stack = createStack();
final Task task = createTask(stack, false /* fakeDraw */);
@@ -226,6 +194,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer();
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
stack.setTaskOrganizer(organizer);
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTrue(stack.isOrganized());
@@ -258,7 +227,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
final Task task2 = createTask(stack2);
final Task stack3 = createStack();
final Task task3 = createTask(stack3);
- final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+ final ITaskOrganizer organizer = registerMockOrganizer();
// First organizer is registered, verify a task appears when changing windowing mode
stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -268,7 +237,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
// Now we replace the registration and1 verify the new organizer receives tasks
// newly entering the windowing mode.
- final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+ final ITaskOrganizer organizer2 = registerMockOrganizer();
stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
// One each for task and task2
verify(organizer2, times(2))
@@ -294,7 +263,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException {
- final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
@@ -313,7 +282,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
final Task task = createTask(stack);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
- final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
+ final ITaskOrganizer organizer = registerMockOrganizer();
verify(organizer, times(1))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
}
@@ -483,8 +452,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
}
};
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
@@ -542,8 +510,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
}
};
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
lastReportedTiles.clear();
@@ -604,10 +571,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
}
};
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
- listener, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
- listener, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
@@ -874,7 +838,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testEnterPipParams() {
final StubOrganizer o = new StubOrganizer();
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o, WINDOWING_MODE_PINNED);
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
final ActivityRecord record = makePipableActivity();
final PictureInPictureParams p = new PictureInPictureParams.Builder()
@@ -895,7 +859,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
}
ChangeSavingOrganizer o = new ChangeSavingOrganizer();
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o, WINDOWING_MODE_PINNED);
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
final ActivityRecord record = makePipableActivity();
final PictureInPictureParams p = new PictureInPictureParams.Builder()
@@ -926,8 +890,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
}
ChangeSavingOrganizer o = new ChangeSavingOrganizer();
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o,
- WINDOWING_MODE_MULTI_WINDOW);
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
final Task stack = createStack();
final Task task = createTask(stack);
@@ -942,22 +905,23 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testPreventDuplicateAppear() throws RemoteException {
final Task stack = createStack();
- final Task task = createTask(stack);
+ final Task task = createTask(stack, false /* fakeDraw */);
final ITaskOrganizer organizer = registerMockOrganizer();
- task.setTaskOrganizer(organizer);
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ stack.setTaskOrganizer(organizer);
// setHasBeenVisible was already called once by the set-up code.
- task.setHasBeenVisible(true);
+ stack.setHasBeenVisible(true);
verify(organizer, times(1))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- task.setTaskOrganizer(null);
+ stack.setTaskOrganizer(null);
verify(organizer, times(1)).onTaskVanished(any());
- task.setTaskOrganizer(organizer);
+ stack.setTaskOrganizer(organizer);
verify(organizer, times(2))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- task.removeImmediately();
+ stack.removeImmediately();
verify(organizer, times(2)).onTaskVanished(any());
}
@@ -966,7 +930,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
final Task stack = createStack();
final Task task = createTask(stack);
final ActivityRecord activity = createActivityRecordInTask(stack.mDisplayContent, task);
- final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+ final ITaskOrganizer organizer = registerMockOrganizer();
// Setup the task to be controlled by the MW mode organizer
stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 5ce61b4e4916..f86d8f15353e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1031,10 +1031,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
TestSplitOrganizer(ActivityTaskManagerService service, int displayId) {
mService = service;
mDisplayId = displayId;
- mService.mTaskOrganizerController.registerTaskOrganizer(this,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- mService.mTaskOrganizerController.registerTaskOrganizer(this,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mService.mTaskOrganizerController.registerTaskOrganizer(this);
WindowContainerToken primary = mService.mTaskOrganizerController.createRootTask(
displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask();
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 8f1d0addbcd8..3104c7e7e0a1 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -123,7 +123,7 @@ public class IorapForwardingService extends SystemService {
try {
iorap = IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"));
} catch (ServiceManager.ServiceNotFoundException e) {
- handleRemoteError(e);
+ Log.w(TAG, e.getMessage());
return null;
}
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index 52e0953813a0..944edd542e40 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -687,10 +687,8 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
method public boolean isDataConnectivityPossible();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledWithReason(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
@@ -722,7 +720,6 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledWithReason(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
@@ -760,10 +757,6 @@ package android.telephony {
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
- field public static final int DATA_ENABLED_REASON_CARRIER = 2; // 0x2
- field public static final int DATA_ENABLED_REASON_POLICY = 1; // 0x1
- field public static final int DATA_ENABLED_REASON_THERMAL = 3; // 0x3
- field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fa229fb47423..a229efbe9970 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4406,7 +4406,7 @@ public class CarrierConfigManager {
});
sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
sDefaults.putAll(Ims.getDefaults());
- sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
+ sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false);
sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
new int[] {4 /* BUSY */});
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index 06c34dcf290a..905f90800305 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -50,6 +50,18 @@ public final class CellIdentityNr extends CellIdentity {
// a list of additional PLMN-IDs reported for this cell
private final ArraySet<String> mAdditionalPlmns;
+ /** @hide */
+ public CellIdentityNr() {
+ super(TAG, CellInfo.TYPE_NR, null, null, null, null);
+ mNrArfcn = CellInfo.UNAVAILABLE;
+ mPci = CellInfo.UNAVAILABLE;
+ mTac = CellInfo.UNAVAILABLE;
+ mNci = CellInfo.UNAVAILABLE;
+ mBands = new int[] {};
+ mAdditionalPlmns = new ArraySet();
+ mGlobalCellId = null;
+ }
+
/**
*
* @param pci Physical Cell Id in range [0, 1007].
diff --git a/telephony/java/android/telephony/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java
index a7e79f93ae89..e01e8f0d5b51 100644
--- a/telephony/java/android/telephony/CellInfoNr.java
+++ b/telephony/java/android/telephony/CellInfoNr.java
@@ -29,9 +29,16 @@ import java.util.Objects;
public final class CellInfoNr extends CellInfo {
private static final String TAG = "CellInfoNr";
- private final CellIdentityNr mCellIdentity;
+ private CellIdentityNr mCellIdentity;
private final CellSignalStrengthNr mCellSignalStrength;
+ /** @hide */
+ public CellInfoNr() {
+ super();
+ mCellIdentity = new CellIdentityNr();
+ mCellSignalStrength = new CellSignalStrengthNr();
+ }
+
private CellInfoNr(Parcel in) {
super(in);
mCellIdentity = CellIdentityNr.CREATOR.createFromParcel(in);
@@ -71,6 +78,11 @@ public final class CellInfoNr extends CellInfo {
return mCellIdentity;
}
+ /** @hide */
+ public void setCellIdentity(CellIdentityNr cid) {
+ mCellIdentity = cid;
+ }
+
/**
* @return a {@link CellSignalStrengthNr} instance.
*/
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f9148d0c44c4..7a7792242c12 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9231,7 +9231,7 @@ public class TelephonyManager {
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @param enable Whether to enable mobile data.
- * @deprecated use setDataEnabledWithReason with reason DATA_ENABLED_REASON_USER instead.
+ * @deprecated use setDataEnabledForReason with reason DATA_ENABLED_REASON_USER instead.
*
*/
@Deprecated
@@ -9243,16 +9243,16 @@ public class TelephonyManager {
/**
* @hide
- * @deprecated use {@link #setDataEnabledWithReason(int, boolean)} instead.
+ * @deprecated use {@link #setDataEnabledForReason(int, boolean)} instead.
*/
@SystemApi
@Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setDataEnabled(int subId, boolean enable) {
try {
- setDataEnabledWithReason(subId, DATA_ENABLED_REASON_USER, enable);
+ setDataEnabledForReason(subId, DATA_ENABLED_REASON_USER, enable);
} catch (RuntimeException e) {
- Log.e(TAG, "Error calling setDataEnabledWithReason e:" + e);
+ Log.e(TAG, "Error calling setDataEnabledForReason e:" + e);
}
}
@@ -9461,9 +9461,9 @@ public class TelephonyManager {
@SystemApi
public boolean getDataEnabled(int subId) {
try {
- return isDataEnabledWithReason(DATA_ENABLED_REASON_USER);
+ return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
} catch (RuntimeException e) {
- Log.e(TAG, "Error calling isDataEnabledWithReason e:" + e);
+ Log.e(TAG, "Error calling isDataEnabledForReason e:" + e);
}
return false;
}
@@ -11016,7 +11016,7 @@ public class TelephonyManager {
*
* @param enabled control enable or disable carrier data.
* @see #resetAllCarrierActions()
- * @deprecated use {@link #setDataEnabledWithReason(int, boolean) with
+ * @deprecated use {@link #setDataEnabledForReason(int, boolean) with
* reason {@link #DATA_ENABLED_REASON_CARRIER}} instead.
* @hide
*/
@@ -11025,9 +11025,9 @@ public class TelephonyManager {
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setCarrierDataEnabled(boolean enabled) {
try {
- setDataEnabledWithReason(DATA_ENABLED_REASON_CARRIER, enabled);
+ setDataEnabledForReason(DATA_ENABLED_REASON_CARRIER, enabled);
} catch (RuntimeException e) {
- Log.e(TAG, "Error calling setDataEnabledWithReason e:" + e);
+ Log.e(TAG, "Error calling setDataEnabledForReason e:" + e);
}
}
@@ -11113,7 +11113,7 @@ public class TelephonyManager {
/**
* Policy control of data connection. Usually used when data limit is passed.
* @param enabled True if enabling the data, otherwise disabling.
- * @deprecated use {@link #setDataEnabledWithReason(int, boolean) with
+ * @deprecated use {@link #setDataEnabledForReason(int, boolean) with
* reason {@link #DATA_ENABLED_REASON_POLICY}} instead.
* @hide
*/
@@ -11121,9 +11121,9 @@ public class TelephonyManager {
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setPolicyDataEnabled(boolean enabled) {
try {
- setDataEnabledWithReason(DATA_ENABLED_REASON_POLICY, enabled);
+ setDataEnabledForReason(DATA_ENABLED_REASON_POLICY, enabled);
} catch (RuntimeException e) {
- Log.e(TAG, "Error calling setDataEnabledWithReason e:" + e);
+ Log.e(TAG, "Error calling setDataEnabledForReason e:" + e);
}
}
@@ -11139,36 +11139,28 @@ public class TelephonyManager {
/**
* To indicate that user enabled or disabled data.
- * @hide
*/
- @SystemApi
public static final int DATA_ENABLED_REASON_USER = 0;
/**
* To indicate that data control due to policy. Usually used when data limit is passed.
* Policy data on/off won't affect user settings but will bypass the
* settings and turns off data internally if set to {@code false}.
- * @hide
*/
- @SystemApi
public static final int DATA_ENABLED_REASON_POLICY = 1;
/**
* To indicate enable or disable carrier data by the system based on carrier signalling or
* carrier privileged apps. Carrier data on/off won't affect user settings but will bypass the
* settings and turns off data internally if set to {@code false}.
- * @hide
*/
- @SystemApi
public static final int DATA_ENABLED_REASON_CARRIER = 2;
/**
* To indicate enable or disable data by thermal service.
* Thermal data on/off won't affect user settings but will bypass the
* settings and turns off data internally if set to {@code false}.
- * @hide
*/
- @SystemApi
public static final int DATA_ENABLED_REASON_THERMAL = 3;
/**
@@ -11197,25 +11189,23 @@ public class TelephonyManager {
* has {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} irrespective of
* the reason.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
- @SystemApi
- public void setDataEnabledWithReason(@DataEnabledReason int reason, boolean enabled) {
- setDataEnabledWithReason(getSubId(), reason, enabled);
+ public void setDataEnabledForReason(@DataEnabledReason int reason, boolean enabled) {
+ setDataEnabledForReason(getSubId(), reason, enabled);
}
- private void setDataEnabledWithReason(int subId, @DataEnabledReason int reason,
+ private void setDataEnabledForReason(int subId, @DataEnabledReason int reason,
boolean enabled) {
try {
ITelephony service = getITelephony();
if (service != null) {
- service.setDataEnabledWithReason(subId, reason, enabled);
+ service.setDataEnabledForReason(subId, reason, enabled);
} else {
throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
- Log.e(TAG, "Telephony#setDataEnabledWithReason RemoteException", ex);
+ Log.e(TAG, "Telephony#setDataEnabledForReason RemoteException", ex);
ex.rethrowFromSystemServer();
}
}
@@ -11223,9 +11213,11 @@ public class TelephonyManager {
/**
* Return whether data is enabled for certain reason .
*
- * If {@link #isDataEnabledWithReason} returns false, it means in data enablement for a
+ * If {@link #isDataEnabledForReason} returns false, it means in data enablement for a
* specific reason is turned off. If any of the reason is off, then it will result in
- * bypassing user preference and result in data to be turned off.
+ * bypassing user preference and result in data to be turned off. Call
+ * {@link #isDataConnectionAllowed} in order to know whether
+ * data connection is allowed on the device.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies
* to the given subId. Otherwise, applies to
@@ -11234,27 +11226,26 @@ public class TelephonyManager {
* @param reason the reason the data enable change is taking place
* @return whether data is enabled for a reason.
* <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
*/
@RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
android.Manifest.permission.READ_PHONE_STATE})
- @SystemApi
- public boolean isDataEnabledWithReason(@DataEnabledReason int reason) {
- return isDataEnabledWithReason(getSubId(), reason);
+ public boolean isDataEnabledForReason(@DataEnabledReason int reason) {
+ return isDataEnabledForReason(getSubId(), reason);
}
- private boolean isDataEnabledWithReason(int subId, @DataEnabledReason int reason) {
+ private boolean isDataEnabledForReason(int subId, @DataEnabledReason int reason) {
try {
ITelephony service = getITelephony();
if (service != null) {
- return service.isDataEnabledWithReason(subId, reason);
+ return service.isDataEnabledForReason(subId, reason);
} else {
throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
- Log.e(TAG, "Telephony#isDataEnabledWithReason RemoteException", ex);
+ Log.e(TAG, "Telephony#isDataEnabledForReason RemoteException", ex);
ex.rethrowFromSystemServer();
}
return false;
@@ -11395,10 +11386,14 @@ public class TelephonyManager {
* <LI>And possibly others.</LI>
* </UL>
* @return {@code true} if the overall data connection is allowed; {@code false} if not.
- * @hide
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} or
+ * android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE
*/
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
+ android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
public boolean isDataConnectionAllowed() {
boolean retVal = false;
try {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e2de5c82940f..4021d0a2888f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1015,7 +1015,7 @@ interface ITelephony {
* @param reason the reason the data enable change is taking place
* @param enable true to turn on, else false
*/
- void setDataEnabledWithReason(int subId, int reason, boolean enable);
+ void setDataEnabledForReason(int subId, int reason, boolean enable);
/**
* Return whether data is enabled for certain reason
@@ -1023,7 +1023,7 @@ interface ITelephony {
* @param reason the reason the data enable change is taking place
* @return true on enabled
*/
- boolean isDataEnabledWithReason(int subId, int reason);
+ boolean isDataEnabledForReason(int subId, int reason);
/**
* Checks if manual network selection is allowed.
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 7790043859a0..05a59ef7fc72 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -744,6 +744,15 @@
</intent-filter>
</activity>
+ <activity android:name="BlurActivity"
+ android:label="Shaders/Blur"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="TextActivity"
android:label="Text/Simple Text"
android:theme="@android:style/Theme.NoTitleBar"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
new file mode 100644
index 000000000000..033fb0ec35d2
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
@@ -0,0 +1,110 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.BlurShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BlurActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ LinearLayout layout = new LinearLayout(this);
+ layout.setClipChildren(false);
+ layout.setGravity(Gravity.CENTER);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500, 500);
+ params.bottomMargin = 100;
+
+ layout.addView(new BlurGradientView(this), params);
+ layout.addView(new BlurView(this), params);
+
+ setContentView(layout);
+ }
+
+ public static class BlurGradientView extends View {
+ private BlurShader mBlurShader = null;
+ private Paint mPaint;
+
+ public BlurGradientView(Context c) {
+ super(c);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (changed || mBlurShader == null) {
+ LinearGradient gradient = new LinearGradient(
+ 0f,
+ 0f,
+ right - left,
+ bottom - top,
+ Color.CYAN,
+ Color.YELLOW,
+ Shader.TileMode.CLAMP
+ );
+ mBlurShader = new BlurShader(30f, 40f, gradient);
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaint.setShader(mBlurShader);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
+ }
+ }
+
+ public static class BlurView extends View {
+
+ private final BlurShader mBlurShader;
+ private final Paint mPaint;
+
+ public BlurView(Context c) {
+ super(c);
+
+ mBlurShader = new BlurShader(20f, 20f, null);
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaint.setShader(mBlurShader);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ mPaint.setColor(Color.BLUE);
+ canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
+
+ mPaint.setColor(Color.RED);
+ canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, 50f, mPaint);
+ }
+ }
+}
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
index 073ae30aaf1a..ca723b881bbd 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
@@ -157,7 +157,7 @@ public class TaskOrganizerMultiWindowTest extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mOrganizer.registerOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+ mOrganizer.registerOrganizer();
mTaskView1 = new ResizingTaskView(this, makeSettingsIntent());
mTaskView2 = new ResizingTaskView(this, makeContactsIntent());
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
index 8fc5c5d78b60..5ec949391181 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
@@ -57,7 +57,7 @@ public class TaskOrganizerPipTest extends Service {
public void onCreate() {
super.onCreate();
- mOrganizer.registerOrganizer(WINDOWING_MODE_PINNED);
+ mOrganizer.registerOrganizer();
final WindowManager.LayoutParams wlp = new WindowManager.LayoutParams();
wlp.setTitle("TaskOrganizerPipTest");
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index ab12ac0ef56e..5a29c2c96ba7 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -26,19 +26,14 @@ import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_OEM;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRODUCT;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_REQUIRED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
-import static android.net.INetd.PERMISSION_INTERNET;
-import static android.net.INetd.PERMISSION_NONE;
-import static android.net.INetd.PERMISSION_SYSTEM;
-import static android.net.INetd.PERMISSION_UNINSTALLED;
-import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
-import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.Process.SYSTEM_UID;
import static com.android.server.connectivity.PermissionMonitor.NETWORK;
import static com.android.server.connectivity.PermissionMonitor.SYSTEM;
-import static com.android.server.connectivity.PermissionMonitor.UidNetdPermissionInfo;
import static junit.framework.Assert.fail;
@@ -69,7 +64,7 @@ import android.net.UidRange;
import android.os.Build;
import android.os.UserHandle;
import android.os.UserManager;
-import android.util.SparseArray;
+import android.util.SparseIntArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -102,6 +97,7 @@ public class PermissionMonitorTest {
private static final int SYSTEM_UID1 = 1000;
private static final int SYSTEM_UID2 = 1008;
private static final int VPN_UID = 10002;
+ private static final String REAL_SYSTEM_PACKAGE_NAME = "android";
private static final String MOCK_PACKAGE1 = "appName1";
private static final String MOCK_PACKAGE2 = "appName2";
private static final String SYSTEM_PACKAGE1 = "sysName1";
@@ -132,7 +128,6 @@ public class PermissionMonitorTest {
new UserInfo(MOCK_USER1, "", 0),
new UserInfo(MOCK_USER2, "", 0),
}));
- doReturn(PackageManager.PERMISSION_DENIED).when(mDeps).uidPermission(anyString(), anyInt());
mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
@@ -145,15 +140,35 @@ public class PermissionMonitorTest {
verify(mMockPmi).getPackageList(mPermissionMonitor);
}
- private boolean wouldBeCarryoverPackage(String partition, int targetSdkVersion, int uid) {
- final PackageInfo packageInfo = buildPackageInfo(partition, uid, MOCK_USER1);
+ private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid,
+ String... permissions) {
+ final PackageInfo packageInfo =
+ packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, permissions, partition);
packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion;
- return mPermissionMonitor.isCarryoverPackage(packageInfo.applicationInfo);
+ packageInfo.applicationInfo.uid = uid;
+ return mPermissionMonitor.hasRestrictedNetworkPermission(packageInfo);
+ }
+
+ private static PackageInfo systemPackageInfoWithPermissions(String... permissions) {
+ return packageInfoWithPermissions(
+ REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM);
}
- private static PackageInfo packageInfoWithPartition(String partition) {
+ private static PackageInfo vendorPackageInfoWithPermissions(String... permissions) {
+ return packageInfoWithPermissions(
+ REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_VENDOR);
+ }
+
+ private static PackageInfo packageInfoWithPermissions(int permissionsFlags,
+ String[] permissions, String partition) {
+ int[] requestedPermissionsFlags = new int[permissions.length];
+ for (int i = 0; i < permissions.length; i++) {
+ requestedPermissionsFlags[i] = permissionsFlags;
+ }
final PackageInfo packageInfo = new PackageInfo();
+ packageInfo.requestedPermissions = permissions;
packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.requestedPermissionsFlags = requestedPermissionsFlags;
int privateFlags = 0;
switch (partition) {
case PARTITION_OEM:
@@ -170,145 +185,168 @@ public class PermissionMonitorTest {
return packageInfo;
}
- private static PackageInfo buildPackageInfo(String partition, int uid, int userId) {
- final PackageInfo pkgInfo = packageInfoWithPartition(partition);
+ private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid, int userId) {
+ final PackageInfo pkgInfo;
+ if (hasSystemPermission) {
+ pkgInfo = systemPackageInfoWithPermissions(
+ CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+ } else {
+ pkgInfo = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, new String[] {}, "");
+ }
pkgInfo.applicationInfo.uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
return pkgInfo;
}
- /** This will REMOVE all previously set permissions from given uid. */
- private void removeAllPermissions(int uid) {
- doReturn(PackageManager.PERMISSION_DENIED).when(mDeps).uidPermission(anyString(), eq(uid));
- }
-
- /** Set up mocks so that given UID has the requested permissions. */
- private void addPermissions(int uid, String... permissions) {
- for (String permission : permissions) {
- doReturn(PackageManager.PERMISSION_GRANTED)
- .when(mDeps).uidPermission(eq(permission), eq(uid));
- }
- }
-
@Test
public void testHasPermission() {
- addPermissions(MOCK_UID1);
- assertFalse(mPermissionMonitor.hasPermission(CHANGE_NETWORK_STATE, MOCK_UID1));
- assertFalse(mPermissionMonitor.hasPermission(NETWORK_STACK, MOCK_UID1));
- assertFalse(mPermissionMonitor.hasPermission(
- CONNECTIVITY_USE_RESTRICTED_NETWORKS, MOCK_UID1));
- assertFalse(mPermissionMonitor.hasPermission(CONNECTIVITY_INTERNAL, MOCK_UID1));
-
- addPermissions(MOCK_UID1, CHANGE_NETWORK_STATE, NETWORK_STACK);
- assertTrue(mPermissionMonitor.hasPermission(CHANGE_NETWORK_STATE, MOCK_UID1));
- assertTrue(mPermissionMonitor.hasPermission(NETWORK_STACK, MOCK_UID1));
- assertFalse(mPermissionMonitor.hasPermission(
- CONNECTIVITY_USE_RESTRICTED_NETWORKS, MOCK_UID1));
- assertFalse(mPermissionMonitor.hasPermission(CONNECTIVITY_INTERNAL, MOCK_UID1));
- assertFalse(mPermissionMonitor.hasPermission(CHANGE_NETWORK_STATE, MOCK_UID2));
- assertFalse(mPermissionMonitor.hasPermission(NETWORK_STACK, MOCK_UID2));
-
- addPermissions(MOCK_UID2, CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL);
- assertFalse(mPermissionMonitor.hasPermission(
- CONNECTIVITY_USE_RESTRICTED_NETWORKS, MOCK_UID1));
- assertFalse(mPermissionMonitor.hasPermission(CONNECTIVITY_INTERNAL, MOCK_UID1));
- assertTrue(mPermissionMonitor.hasPermission(
- CONNECTIVITY_USE_RESTRICTED_NETWORKS, MOCK_UID2));
- assertTrue(mPermissionMonitor.hasPermission(CONNECTIVITY_INTERNAL, MOCK_UID2));
+ PackageInfo app = systemPackageInfoWithPermissions();
+ assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+ assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+ app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE, NETWORK_STACK);
+ assertTrue(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+ assertTrue(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+ app = systemPackageInfoWithPermissions(
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL);
+ assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+ assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+ assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+ app = packageInfoWithPermissions(REQUESTED_PERMISSION_REQUIRED, new String[] {
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL, NETWORK_STACK },
+ PARTITION_SYSTEM);
+ assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+ assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+ app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE);
+ app.requestedPermissions = null;
+ assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+
+ app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE);
+ app.requestedPermissionsFlags = null;
+ assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
}
@Test
public void testIsVendorApp() {
- PackageInfo app = packageInfoWithPartition(PARTITION_SYSTEM);
+ PackageInfo app = systemPackageInfoWithPermissions();
assertFalse(mPermissionMonitor.isVendorApp(app.applicationInfo));
- app = packageInfoWithPartition(PARTITION_OEM);
+ app = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED,
+ new String[] {}, PARTITION_OEM);
assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo));
- app = packageInfoWithPartition(PARTITION_PRODUCT);
+ app = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED,
+ new String[] {}, PARTITION_PRODUCT);
assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo));
- app = packageInfoWithPartition(PARTITION_VENDOR);
+ app = vendorPackageInfoWithPermissions();
assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo));
}
- /**
- * Remove all permissions from the uid then setup permissions to uid for checking restricted
- * network permission.
- */
- private void assertRestrictedNetworkPermission(boolean hasPermission, int uid,
- String... permissions) {
- removeAllPermissions(uid);
- addPermissions(uid, permissions);
- assertEquals(hasPermission, mPermissionMonitor.hasRestrictedNetworkPermission(uid));
+ @Test
+ public void testHasNetworkPermission() {
+ PackageInfo app = systemPackageInfoWithPermissions();
+ assertFalse(mPermissionMonitor.hasNetworkPermission(app));
+ app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE);
+ assertTrue(mPermissionMonitor.hasNetworkPermission(app));
+ app = systemPackageInfoWithPermissions(NETWORK_STACK);
+ assertFalse(mPermissionMonitor.hasNetworkPermission(app));
+ app = systemPackageInfoWithPermissions(CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+ assertFalse(mPermissionMonitor.hasNetworkPermission(app));
+ app = systemPackageInfoWithPermissions(CONNECTIVITY_INTERNAL);
+ assertFalse(mPermissionMonitor.hasNetworkPermission(app));
}
@Test
public void testHasRestrictedNetworkPermission() {
- assertRestrictedNetworkPermission(false, MOCK_UID1);
- assertRestrictedNetworkPermission(false, MOCK_UID1, CHANGE_NETWORK_STATE);
- assertRestrictedNetworkPermission(true, MOCK_UID1, NETWORK_STACK);
- assertRestrictedNetworkPermission(false, MOCK_UID1, CONNECTIVITY_INTERNAL);
- assertRestrictedNetworkPermission(true, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
- assertRestrictedNetworkPermission(false, MOCK_UID1, CHANGE_WIFI_STATE);
- assertRestrictedNetworkPermission(true, MOCK_UID1, PERMISSION_MAINLINE_NETWORK_STACK);
-
- assertFalse(mPermissionMonitor.hasRestrictedNetworkPermission(MOCK_UID2));
- assertFalse(mPermissionMonitor.hasRestrictedNetworkPermission(SYSTEM_UID));
+ assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
+ assertFalse(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
+ assertFalse(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertFalse(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
+
+ assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
+ assertFalse(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL));
}
@Test
- public void testIsCarryoverPackage() {
+ public void testHasRestrictedNetworkPermissionSystemUid() {
doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
- assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
- assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
- assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
- assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
- assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
- assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+ assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
- assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
- assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
- assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
-
- assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, MOCK_UID1));
- assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID1));
+ assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
+ assertFalse(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ }
+
+ @Test
+ public void testHasRestrictedNetworkPermissionVendorApp() {
+ assertTrue(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
+
+ assertFalse(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+ assertFalse(hasRestrictedNetworkPermission(
+ PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ assertFalse(hasRestrictedNetworkPermission(
+ PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_NETWORK_STATE));
}
private void assertBackgroundPermission(boolean hasPermission, String name, int uid,
String... permissions) throws Exception {
when(mPackageManager.getPackageInfo(eq(name), anyInt()))
- .thenReturn(buildPackageInfo(PARTITION_SYSTEM, uid, MOCK_USER1));
- addPermissions(uid, permissions);
+ .thenReturn(packageInfoWithPermissions(
+ REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM));
mPermissionMonitor.onPackageAdded(name, uid);
assertEquals(hasPermission, mPermissionMonitor.hasUseBackgroundNetworksPermission(uid));
}
@Test
public void testHasUseBackgroundNetworksPermission() throws Exception {
- assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID1));
- assertBackgroundPermission(false, "mock1", MOCK_UID1);
- assertBackgroundPermission(false, "mock2", MOCK_UID1, CONNECTIVITY_INTERNAL);
- assertBackgroundPermission(true, "mock3", MOCK_UID1, NETWORK_STACK);
+ assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(SYSTEM_UID));
+ assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID);
+ assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID, CONNECTIVITY_INTERNAL);
+ assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, CHANGE_NETWORK_STATE);
+ assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, NETWORK_STACK);
- assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2));
- assertBackgroundPermission(false, "mock4", MOCK_UID2);
- assertBackgroundPermission(true, "mock5", MOCK_UID2,
+ assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID1));
+ assertBackgroundPermission(false, MOCK_PACKAGE1, MOCK_UID1);
+ assertBackgroundPermission(true, MOCK_PACKAGE1, MOCK_UID1,
CONNECTIVITY_USE_RESTRICTED_NETWORKS);
- doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
- assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(SYSTEM_UID));
- assertBackgroundPermission(false, "system1", SYSTEM_UID);
- assertBackgroundPermission(true, "system2", SYSTEM_UID, CHANGE_NETWORK_STATE);
- doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
- removeAllPermissions(SYSTEM_UID);
- assertBackgroundPermission(true, "system3", SYSTEM_UID);
+ assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2));
+ assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2);
+ assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2,
+ CONNECTIVITY_INTERNAL);
+ assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID2, NETWORK_STACK);
}
private class NetdMonitor {
@@ -318,7 +356,7 @@ public class PermissionMonitorTest {
// Add hook to verify and track result of setPermission.
doAnswer((InvocationOnMock invocation) -> {
final Object[] args = invocation.getArguments();
- final Boolean isSystem = args[0].equals(PERMISSION_SYSTEM);
+ final Boolean isSystem = args[0].equals(INetd.PERMISSION_SYSTEM);
for (final int uid : (int[]) args[1]) {
// TODO: Currently, permission monitor will send duplicate commands for each uid
// corresponding to each user. Need to fix that and uncomment below test.
@@ -378,14 +416,13 @@ public class PermissionMonitorTest {
// MOCK_UID1: MOCK_PACKAGE1 only has network permission.
// SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
// SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission.
- doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM),
- anyString(), anyInt());
+ doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(),
- eq(SYSTEM_PACKAGE1), anyInt());
+ eq(SYSTEM_PACKAGE1));
doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
- eq(SYSTEM_PACKAGE2), anyInt());
+ eq(SYSTEM_PACKAGE2));
doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
- eq(MOCK_PACKAGE1), anyInt());
+ eq(MOCK_PACKAGE1));
// Add SYSTEM_PACKAGE2, expect only have network permission.
mPermissionMonitor.onUserAdded(MOCK_USER1);
@@ -436,15 +473,13 @@ public class PermissionMonitorTest {
public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
- buildPackageInfo(PARTITION_SYSTEM, SYSTEM_UID1, MOCK_USER1),
- buildPackageInfo(PARTITION_SYSTEM, MOCK_UID1, MOCK_USER1),
- buildPackageInfo(PARTITION_SYSTEM, MOCK_UID2, MOCK_USER1),
- buildPackageInfo(PARTITION_SYSTEM, VPN_UID, MOCK_USER1)
+ buildPackageInfo(/* SYSTEM */ true, SYSTEM_UID1, MOCK_USER1),
+ buildPackageInfo(/* SYSTEM */ false, MOCK_UID1, MOCK_USER1),
+ buildPackageInfo(/* SYSTEM */ false, MOCK_UID2, MOCK_USER1),
+ buildPackageInfo(/* SYSTEM */ false, VPN_UID, MOCK_USER1)
}));
when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
- buildPackageInfo(PARTITION_SYSTEM, MOCK_UID1, MOCK_USER1));
- addPermissions(SYSTEM_UID,
- CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+ buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring();
// Every app on user 0 except MOCK_UID2 are under VPN.
final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
@@ -489,11 +524,11 @@ public class PermissionMonitorTest {
public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
- buildPackageInfo(PARTITION_SYSTEM, SYSTEM_UID1, MOCK_USER1),
- buildPackageInfo(PARTITION_SYSTEM, VPN_UID, MOCK_USER1)
+ buildPackageInfo(true, SYSTEM_UID1, MOCK_USER1),
+ buildPackageInfo(false, VPN_UID, MOCK_USER1)
}));
when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
- buildPackageInfo(PARTITION_SYSTEM, MOCK_UID1, MOCK_USER1));
+ buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring();
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
@@ -561,48 +596,47 @@ public class PermissionMonitorTest {
// SYSTEM_UID1: SYSTEM_PACKAGE1 has internet permission and update device stats permission.
// SYSTEM_UID2: SYSTEM_PACKAGE2 has only update device stats permission.
- final SparseArray<UidNetdPermissionInfo> uidsPermInfo = new SparseArray<>();
- uidsPermInfo.put(MOCK_UID1, new UidNetdPermissionInfo(PERMISSION_INTERNET));
- uidsPermInfo.put(MOCK_UID2, new UidNetdPermissionInfo(PERMISSION_NONE));
- uidsPermInfo.put(SYSTEM_UID1, new UidNetdPermissionInfo(
- PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS));
- uidsPermInfo.put(SYSTEM_UID2, new UidNetdPermissionInfo(PERMISSION_UPDATE_DEVICE_STATS));
+ SparseIntArray netdPermissionsAppIds = new SparseIntArray();
+ netdPermissionsAppIds.put(MOCK_UID1, INetd.PERMISSION_INTERNET);
+ netdPermissionsAppIds.put(MOCK_UID2, INetd.PERMISSION_NONE);
+ netdPermissionsAppIds.put(SYSTEM_UID1, INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS);
+ netdPermissionsAppIds.put(SYSTEM_UID2, INetd.PERMISSION_UPDATE_DEVICE_STATS);
// Send the permission information to netd, expect permission updated.
- mPermissionMonitor.sendPackagePermissionsToNetd(uidsPermInfo);
+ mPermissionMonitor.sendPackagePermissionsToNetd(netdPermissionsAppIds);
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET,
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET,
new int[]{MOCK_UID1});
- mNetdServiceMonitor.expectPermission(PERMISSION_NONE, new int[]{MOCK_UID2});
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
- | PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID1});
- mNetdServiceMonitor.expectPermission(PERMISSION_UPDATE_DEVICE_STATS,
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID2});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UPDATE_DEVICE_STATS,
new int[]{SYSTEM_UID2});
// Update permission of MOCK_UID1, expect new permission show up.
- mPermissionMonitor.sendPackagePermissionsForUid(MOCK_UID1, new UidNetdPermissionInfo(
- PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS));
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
- | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+ mPermissionMonitor.sendPackagePermissionsForUid(MOCK_UID1,
+ INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
// Change permissions of SYSTEM_UID2, expect new permission show up and old permission
// revoked.
- mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID2, new UidNetdPermissionInfo(
- PERMISSION_INTERNET));
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{SYSTEM_UID2});
+ mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID2,
+ INetd.PERMISSION_INTERNET);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{SYSTEM_UID2});
// Revoke permission from SYSTEM_UID1, expect no permission stored.
- mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, new UidNetdPermissionInfo(
- PERMISSION_NONE));
- mNetdServiceMonitor.expectPermission(PERMISSION_NONE, new int[]{SYSTEM_UID1});
+ mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, INetd.PERMISSION_NONE);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{SYSTEM_UID1});
}
private PackageInfo setPackagePermissions(String packageName, int uid, String[] permissions)
throws Exception {
- final PackageInfo packageInfo = buildPackageInfo(PARTITION_SYSTEM, uid, MOCK_USER1);
+ PackageInfo packageInfo = packageInfoWithPermissions(
+ REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM);
when(mPackageManager.getPackageInfo(eq(packageName), anyInt())).thenReturn(packageInfo);
when(mPackageManager.getPackagesForUid(eq(uid))).thenReturn(new String[]{packageName});
- addPermissions(uid, permissions);
return packageInfo;
}
@@ -618,30 +652,31 @@ public class PermissionMonitorTest {
final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
- | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
addPackage(MOCK_PACKAGE2, MOCK_UID2, new String[] {INTERNET});
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID2});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID2});
}
@Test
public void testPackageInstallSharedUid() throws Exception {
final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
- addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
- | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+ PackageInfo packageInfo1 = addPackage(MOCK_PACKAGE1, MOCK_UID1,
+ new String[] {INTERNET, UPDATE_DEVICE_STATS});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
// Install another package with the same uid and no permissions should not cause the UID to
// lose permissions.
- final PackageInfo packageInfo2 = buildPackageInfo(PARTITION_SYSTEM, MOCK_UID1, MOCK_USER1);
+ PackageInfo packageInfo2 = systemPackageInfoWithPermissions();
when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
when(mPackageManager.getPackagesForUid(MOCK_UID1))
.thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2});
mPermissionMonitor.onPackageAdded(MOCK_PACKAGE2, MOCK_UID1);
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
- | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
}
@Test
@@ -649,12 +684,12 @@ public class PermissionMonitorTest {
final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
- | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
- mNetdServiceMonitor.expectPermission(PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
}
@Test
@@ -662,16 +697,15 @@ public class PermissionMonitorTest {
final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
- | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
- removeAllPermissions(MOCK_UID1);
mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
- mNetdServiceMonitor.expectPermission(PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
}
@Test
@@ -679,10 +713,10 @@ public class PermissionMonitorTest {
final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {});
- mNetdServiceMonitor.expectPermission(PERMISSION_NONE, new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID1});
addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
}
@Test
@@ -690,19 +724,17 @@ public class PermissionMonitorTest {
final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
- | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
// Mock another package with the same uid but different permissions.
- final PackageInfo packageInfo2 = buildPackageInfo(PARTITION_SYSTEM, MOCK_UID1, MOCK_USER1);
+ PackageInfo packageInfo2 = systemPackageInfoWithPermissions(INTERNET);
when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{
MOCK_PACKAGE2});
- removeAllPermissions(MOCK_UID1);
- addPermissions(MOCK_UID1, INTERNET);
mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
- mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
}
@Test
@@ -711,6 +743,9 @@ public class PermissionMonitorTest {
// necessary permission.
final Context realContext = InstrumentationRegistry.getContext();
final PermissionMonitor monitor = new PermissionMonitor(realContext, mNetdService);
- assertTrue(monitor.hasPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, SYSTEM_UID));
+ final PackageManager manager = realContext.getPackageManager();
+ final PackageInfo systemInfo = manager.getPackageInfo(REAL_SYSTEM_PACKAGE_NAME,
+ GET_PERMISSIONS | MATCH_ANY_USER);
+ assertTrue(monitor.hasPermission(systemInfo, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
}
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 6dc4fced19a2..8f093779da11 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -30,6 +30,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.net.NetworkTemplate;
import android.os.test.TestLooper;
@@ -135,6 +136,11 @@ public final class NetworkStatsSubscriptionsMonitorTest {
mMonitor.onSubscriptionsChanged();
}
+ private void updateSubscriberIdForTestSub(int subId, @Nullable final String subscriberId) {
+ when(mTelephonyManager.getSubscriberId(subId)).thenReturn(subscriberId);
+ mMonitor.onSubscriptionsChanged();
+ }
+
private void removeTestSub(int subId) {
// Remove subId from TestSubList.
mTestSubList.removeIf(it -> it == subId);
@@ -268,4 +274,54 @@ public final class NetworkStatsSubscriptionsMonitorTest {
listener.onServiceStateChanged(serviceState);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
}
+
+ @Test
+ public void testSubscriberIdUnavailable() {
+ final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+ ArgumentCaptor.forClass(RatTypeListener.class);
+
+ mMonitor.start();
+ // Insert sim1, set subscriberId to null which is normal in SIM PIN locked case.
+ // Verify RAT type is NETWORK_TYPE_UNKNOWN and service will not perform listener
+ // registration.
+ addTestSub(TEST_SUBID1, null);
+ verify(mTelephonyManager, never()).listen(any(), anyInt());
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+
+ // Set IMSI for sim1, verify the listener will be registered.
+ updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1);
+ verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
+ eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ reset(mTelephonyManager);
+ when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+
+ // Set RAT type of sim1 to UMTS. Verify RAT type of sim1 is changed.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+ TelephonyManager.NETWORK_TYPE_UMTS);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ reset(mDelegate);
+
+ // Set IMSI to null again to simulate somehow IMSI is not available, such as
+ // modem crash. Verify service should not unregister listener.
+ updateSubscriberIdForTestSub(TEST_SUBID1, null);
+ verify(mTelephonyManager, never()).listen(any(), anyInt());
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ reset(mDelegate);
+
+ // Set RAT type of sim1 to LTE. Verify RAT type of sim1 is still changed even if the IMSI
+ // is not available. The monitor keeps the listener even if the IMSI disappears because
+ // the IMSI can never change for any given subId, therefore even if the IMSI is updated
+ // to null, the monitor should continue accepting updates of the RAT type. However,
+ // telephony is never actually supposed to do this, if the IMSI disappears there should
+ // not be updates, but it's still the right thing to do theoretically.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+ TelephonyManager.NETWORK_TYPE_LTE);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
+ reset(mDelegate);
+
+ mMonitor.stop();
+ verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
+ eq(PhoneStateListener.LISTEN_NONE));
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ }
}