summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp6
-rw-r--r--apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java2
-rw-r--r--apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java111
-rw-r--r--apct-tests/perftests/packagemanager/Android.bp21
-rw-r--r--apct-tests/perftests/packagemanager/AndroidManifest.xml89
-rw-r--r--apct-tests/perftests/packagemanager/AndroidTest.xml88
-rw-r--r--apct-tests/perftests/packagemanager/apps/query-all/Android.bp314
-rw-r--r--apct-tests/perftests/packagemanager/apps/query-all/AndroidManifest.xml81
-rw-r--r--apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java189
-rw-r--r--apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java51
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java48
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java75
-rw-r--r--apex/sdkextensions/framework/java/android/os/ext/test/Test.java53
-rw-r--r--api/current.txt42
-rw-r--r--api/module-lib-current.txt129
-rwxr-xr-xapi/system-current.txt63
-rw-r--r--api/test-current.txt1
-rw-r--r--cmds/idmap2/include/idmap2/ResourceUtils.h4
-rw-r--r--cmds/idmap2/libidmap2/ResourceMapping.cpp17
-rw-r--r--cmds/idmap2/libidmap2/ResourceUtils.cpp6
-rw-r--r--cmds/idmap2/tests/FileUtilsTests.cpp4
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp37
-rwxr-xr-xcmds/idmap2/tests/data/overlay/build8
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-shared.apkbin0 -> 3757 bytes
-rw-r--r--cmds/statsd/Android.bp6
-rw-r--r--cmds/statsd/android.frameworks.stats@1.0-service.xml11
-rw-r--r--cmds/statsd/src/StatsService.cpp98
-rw-r--r--cmds/statsd/src/StatsService.h61
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp31
-rw-r--r--cmds/statsd/src/logd/LogEvent.h6
-rw-r--r--cmds/statsd/src/main.cpp9
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java101
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl4
-rw-r--r--core/java/android/app/ActivityManager.java5
-rw-r--r--core/java/android/app/ApplicationExitInfo.java38
-rw-r--r--core/java/android/app/ITaskOrganizerController.aidl3
-rw-r--r--core/java/android/app/WindowContext.java4
-rw-r--r--core/java/android/app/admin/DevicePolicyKeyguardService.java14
-rw-r--r--core/java/android/app/admin/IKeyguardCallback.aidl4
-rw-r--r--core/java/android/content/pm/PackageInfoLite.java7
-rw-r--r--core/java/android/content/pm/PackageManager.java13
-rw-r--r--core/java/android/content/rollback/RollbackManager.java4
-rw-r--r--core/java/android/hardware/soundtrigger/ConversionUtil.java11
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java8
-rw-r--r--core/java/android/net/ConnectivityDiagnosticsManager.java2
-rw-r--r--core/java/android/net/ConnectivityManager.java6
-rw-r--r--core/java/android/os/Trace.java2
-rw-r--r--core/java/android/os/UserManager.java8
-rw-r--r--core/java/android/os/incremental/IncrementalManager.java8
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureService.java6
-rw-r--r--core/java/android/service/quicksettings/TileService.java14
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java4
-rw-r--r--core/java/android/util/apk/ApkSignatureVerifier.java275
-rw-r--r--core/java/android/view/SurfaceControlViewHost.aidl19
-rw-r--r--core/java/android/view/ViewRootImpl.java10
-rw-r--r--core/java/android/view/WindowContainerTransaction.java122
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java7
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java7
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionInfo.java1
-rw-r--r--core/java/android/webkit/WebResourceRequest.java6
-rw-r--r--core/java/android/widget/TextView.java7
-rw-r--r--core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java4
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java1
-rw-r--r--core/java/com/android/internal/content/NativeLibraryHelper.java111
-rw-r--r--core/java/com/android/server/SystemConfig.java5
-rw-r--r--core/jni/Android.bp2
-rw-r--r--core/jni/AndroidRuntime.cpp282
-rw-r--r--core/jni/android/graphics/FontFamily.cpp4
-rw-r--r--core/jni/android/graphics/fonts/Font.cpp4
-rw-r--r--core/jni/android_media_AudioDevice.cpp (renamed from core/jni/android_media_AudioDeviceAddress.cpp)30
-rw-r--r--core/jni/android_media_AudioDevice.h (renamed from core/jni/android_media_AudioDeviceAddress.h)12
-rw-r--r--core/jni/android_media_AudioSystem.cpp228
-rw-r--r--core/jni/android_os_incremental_IncrementalManager.cpp8
-rw-r--r--core/proto/android/app/appexitinfo.proto4
-rw-r--r--core/proto/android/app/settings_enums.proto3
-rw-r--r--core/proto/android/server/jobscheduler.proto8
-rw-r--r--core/res/AndroidManifest.xml18
-rw-r--r--core/res/res/drawable/tab_indicator_resolver.xml35
-rw-r--r--core/res/res/layout/accessibility_button_chooser_item.xml1
-rw-r--r--core/res/res/layout/tab_indicator_resolver.xml38
-rw-r--r--core/res/res/values-night/colors.xml2
-rw-r--r--core/res/res/values/themes_device_defaults.xml9
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java7
-rw-r--r--libs/hwui/RenderNode.cpp2
-rw-r--r--libs/hwui/RenderNode.h3
-rw-r--r--libs/hwui/hwui/ImageDecoder.cpp15
-rw-r--r--libs/hwui/hwui/ImageDecoder.h1
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.h2
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp3
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp2
-rw-r--r--libs/hwui/tests/unit/FatVectorTests.cpp2
-rw-r--r--libs/hwui/utils/FatVector.h105
-rw-r--r--media/java/android/media/AudioDevice.aidl (renamed from media/java/android/media/AudioDeviceAddress.aidl)2
-rw-r--r--media/java/android/media/AudioDevice.java (renamed from media/java/android/media/AudioDeviceAddress.java)34
-rw-r--r--media/java/android/media/AudioManager.java10
-rw-r--r--media/java/android/media/AudioSystem.java16
-rw-r--r--media/java/android/media/IAudioService.aidl8
-rw-r--r--media/java/android/media/MediaRouter2.java13
-rw-r--r--media/java/android/media/audiofx/AudioEffect.java6
-rw-r--r--media/java/android/media/soundtrigger/SoundTriggerDetector.java11
-rw-r--r--media/java/android/media/soundtrigger/SoundTriggerManager.java37
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java12
-rw-r--r--media/jni/android_media_MediaCodec.cpp32
-rw-r--r--media/jni/android_media_tv_Tuner.cpp178
-rw-r--r--media/jni/android_media_tv_Tuner.h11
-rw-r--r--native/graphics/jni/imagedecoder.cpp6
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java36
-rw-r--r--packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml25
-rw-r--r--packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_grey.xml25
-rw-r--r--packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml92
-rw-r--r--packages/SettingsLib/res/values/config.xml24
-rw-r--r--packages/SettingsLib/res/values/strings.xml8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java50
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java147
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java42
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java88
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java97
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java47
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java42
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java45
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java16
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java47
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java63
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java57
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java36
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java83
-rw-r--r--packages/Shell/AndroidManifest.xml16
-rw-r--r--packages/Shell/res/layout/null_home_finishing_boot.xml44
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java122
-rw-r--r--packages/Shell/src/com/android/shell/NullHome.java43
-rw-r--r--packages/SystemUI/res-keyguard/values/config.xml6
-rw-r--r--packages/SystemUI/res/layout/controls_icon.xml26
-rw-r--r--packages/SystemUI/res/layout/controls_no_favorites.xml52
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java112
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java421
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleMenuView.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/Event.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/RichEvent.java123
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/SysuiLog.java180
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt100
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java164
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifDismissInterceptor.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java212
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java115
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java143
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java842
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java111
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java204
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java6
-rw-r--r--packages/Tethering/common/TetheringLib/Android.bp31
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java3
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java14
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java16
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java14
-rw-r--r--packages/Tethering/tests/unit/Android.bp8
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java13
-rw-r--r--packages/overlays/Android.mk1
-rw-r--r--packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.mk14
-rw-r--r--packages/overlays/DisplayCutoutEmulationWaterfallOverlay/AndroidManifest.xml26
-rw-r--r--packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml22
-rw-r--r--packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml35
-rw-r--r--packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/strings.xml21
-rw-r--r--services/Android.bp2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java76
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java17
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java2
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java15
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java12
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java52
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java111
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java22
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java8
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java8
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java12
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java8
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java58
-rw-r--r--services/core/java/com/android/server/display/WifiDisplayController.java6
-rw-r--r--services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java91
-rw-r--r--services/core/java/com/android/server/media/BluetoothRouteProvider.java22
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java45
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelperImpl.java44
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java13
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java64
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java3
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java39
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java2
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java21
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java104
-rw-r--r--services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java72
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java100
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java15
-rw-r--r--services/core/jni/Android.bp2
-rw-r--r--services/core/jni/com_android_server_SystemServer.cpp7
-rw-r--r--services/people/java/com/android/server/people/data/CallLogQueryHelper.java2
-rw-r--r--services/people/java/com/android/server/people/data/ConversationInfo.java20
-rw-r--r--services/people/java/com/android/server/people/data/ConversationStore.java18
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java99
-rw-r--r--services/people/java/com/android/server/people/data/Event.java77
-rw-r--r--services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java158
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java44
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java102
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java295
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java86
-rw-r--r--services/tests/wmtests/AndroidManifest.xml1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java76
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java60
-rw-r--r--telephony/common/com/google/android/mms/pdu/CharacterSets.java168
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java3
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java5
-rw-r--r--wifi/Android.bp2
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl4
-rw-r--r--wifi/java/android/net/wifi/WifiInfo.java5
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java62
-rwxr-xr-xwifi/java/android/net/wifi/WifiOemMigrationHook.java (renamed from wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java)63
-rw-r--r--wifi/java/android/net/wifi/p2p/WifiP2pConfig.java2
-rw-r--r--wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java6
-rw-r--r--wifi/java/android/net/wifi/p2p/WifiP2pManager.java29
-rw-r--r--wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java25
-rw-r--r--wifi/java/com/android/server/wifi/BaseWifiService.java641
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java10
-rw-r--r--wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java2
-rw-r--r--wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java4
282 files changed, 7841 insertions, 4461 deletions
diff --git a/Android.bp b/Android.bp
index 12bc90680dd5..69d654fbddbc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -403,7 +403,6 @@ java_defaults {
"unsupportedappusage",
"framework-media-stubs-systemapi",
"framework-mediaprovider-stubs-systemapi",
- "framework-tethering",
"framework-telephony-stubs",
],
@@ -472,6 +471,7 @@ java_library {
"framework-permission-stubs-systemapi",
"framework-wifi-stubs",
"ike-stubs",
+ "framework-tethering-stubs",
],
installable: true,
javac_shard_size: 150,
@@ -496,6 +496,7 @@ java_library {
"//frameworks/base/apex/blobstore/framework",
"//frameworks/base/apex/jobscheduler/framework",
"//frameworks/base/apex/statsd/service",
+ "//frameworks/base/packages/Tethering/tests/unit",
],
}
@@ -523,8 +524,7 @@ java_library {
"framework-statsd",
"framework-wifi-stubs",
"ike-stubs",
- // TODO(b/147200698): should be the stub of framework-tethering
- "framework-tethering",
+ "framework-tethering-stubs",
// TODO (b/147688669) should be framework-telephony-stubs
"framework-telephony",
// TODO(jiyong): add stubs for APEXes here
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index a7b69c420c59..8e0ea9888e4c 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -102,7 +102,7 @@ public class BlobStorePerfTests {
durations.clear();
collectDigestDurationsFromTrace(parser, durations);
- // get and delete blobId
+ // TODO: get and delete blobId before next iteration.
}
} finally {
mAtraceUtils.stopTrace();
diff --git a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
deleted file mode 100644
index 236f548cf6dd..000000000000
--- a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2017 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.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class PackageManagerPerfTest {
- private static final String PERMISSION_NAME_EXISTS =
- "com.android.perftests.core.TestPermission";
- private static final String PERMISSION_NAME_DOESNT_EXIST =
- "com.android.perftests.core.TestBadPermission";
- private static final ComponentName TEST_ACTIVITY =
- new ComponentName("com.android.perftests.core",
- "android.perftests.utils.PerfTestActivity");
-
- @Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
-
- @Test
- public void testCheckPermissionExists() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
- final String packageName = TEST_ACTIVITY.getPackageName();
-
- while (state.keepRunning()) {
- int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName);
- }
- }
-
- @Test
- public void testCheckPermissionDoesntExist() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
- final String packageName = TEST_ACTIVITY.getPackageName();
-
- while (state.keepRunning()) {
- int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName);
- }
- }
-
- @Test
- public void testQueryIntentActivities() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
- final Intent intent = new Intent("com.android.perftests.core.PERFTEST");
-
- while (state.keepRunning()) {
- pm.queryIntentActivities(intent, 0);
- }
- }
-
- @Test
- public void testGetPackageInfo() throws Exception {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
- final String packageName = TEST_ACTIVITY.getPackageName();
-
- while (state.keepRunning()) {
- pm.getPackageInfo(packageName, 0);
- }
- }
-
- @Test
- public void testGetApplicationInfo() throws Exception {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
- final String packageName = TEST_ACTIVITY.getPackageName();
-
- while (state.keepRunning()) {
- pm.getApplicationInfo(packageName, 0);
- }
- }
-
- @Test
- public void testGetActivityInfo() throws Exception {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
-
- while (state.keepRunning()) {
- pm.getActivityInfo(TEST_ACTIVITY, 0);
- }
- }
-}
diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp
new file mode 100644
index 000000000000..17033e048c7d
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/Android.bp
@@ -0,0 +1,21 @@
+android_test {
+ name: "PackageManagerPerfTests",
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "platform-compat-test-rules",
+ "androidx.appcompat_appcompat",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "androidx.annotation_annotation",
+ "apct-perftests-utils",
+ ],
+
+ libs: ["android.test.base"],
+
+ platform_apis: true,
+
+ test_suites: ["device-tests"],
+
+}
diff --git a/apct-tests/perftests/packagemanager/AndroidManifest.xml b/apct-tests/perftests/packagemanager/AndroidManifest.xml
new file mode 100644
index 000000000000..520f4b55d931
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/AndroidManifest.xml
@@ -0,0 +1,89 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.perftests.packagemanager">
+
+ <permission android:name="com.android.perftests.packagemanager.TestPermission" />
+ <uses-permission android:name="com.android.perftests.packagemanager.TestPermission" />
+
+ <queries>
+ <package android:name="com.android.perftests.appenumeration0" />
+ <package android:name="com.android.perftests.appenumeration1" />
+ <package android:name="com.android.perftests.appenumeration2" />
+ <package android:name="com.android.perftests.appenumeration3" />
+ <package android:name="com.android.perftests.appenumeration4" />
+ <package android:name="com.android.perftests.appenumeration5" />
+ <package android:name="com.android.perftests.appenumeration6" />
+ <package android:name="com.android.perftests.appenumeration7" />
+ <package android:name="com.android.perftests.appenumeration8" />
+ <package android:name="com.android.perftests.appenumeration9" />
+ <package android:name="com.android.perftests.appenumeration10" />
+ <package android:name="com.android.perftests.appenumeration11" />
+ <package android:name="com.android.perftests.appenumeration12" />
+ <package android:name="com.android.perftests.appenumeration13" />
+ <package android:name="com.android.perftests.appenumeration14" />
+ <package android:name="com.android.perftests.appenumeration15" />
+ <package android:name="com.android.perftests.appenumeration16" />
+ <package android:name="com.android.perftests.appenumeration17" />
+ <package android:name="com.android.perftests.appenumeration18" />
+ <package android:name="com.android.perftests.appenumeration19" />
+ <package android:name="com.android.perftests.appenumeration20" />
+ <package android:name="com.android.perftests.appenumeration21" />
+ <package android:name="com.android.perftests.appenumeration22" />
+ <package android:name="com.android.perftests.appenumeration23" />
+ <package android:name="com.android.perftests.appenumeration24" />
+ <package android:name="com.android.perftests.appenumeration25" />
+ <package android:name="com.android.perftests.appenumeration26" />
+ <package android:name="com.android.perftests.appenumeration27" />
+ <package android:name="com.android.perftests.appenumeration28" />
+ <package android:name="com.android.perftests.appenumeration29" />
+ <package android:name="com.android.perftests.appenumeration30" />
+ <package android:name="com.android.perftests.appenumeration31" />
+ <package android:name="com.android.perftests.appenumeration32" />
+ <package android:name="com.android.perftests.appenumeration33" />
+ <package android:name="com.android.perftests.appenumeration34" />
+ <package android:name="com.android.perftests.appenumeration35" />
+ <package android:name="com.android.perftests.appenumeration36" />
+ <package android:name="com.android.perftests.appenumeration37" />
+ <package android:name="com.android.perftests.appenumeration38" />
+ <package android:name="com.android.perftests.appenumeration39" />
+ <package android:name="com.android.perftests.appenumeration40" />
+ <package android:name="com.android.perftests.appenumeration41" />
+ <package android:name="com.android.perftests.appenumeration42" />
+ <package android:name="com.android.perftests.appenumeration43" />
+ <package android:name="com.android.perftests.appenumeration44" />
+ <package android:name="com.android.perftests.appenumeration45" />
+ <package android:name="com.android.perftests.appenumeration46" />
+ <package android:name="com.android.perftests.appenumeration47" />
+ <package android:name="com.android.perftests.appenumeration48" />
+ <package android:name="com.android.perftests.appenumeration49" />
+ </queries>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.perftests.utils.PerfTestActivity">
+ <intent-filter>
+ <action android:name="com.android.perftests.packagemanager.PERFTEST" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.perftests.packagemanager"/>
+
+</manifest>
diff --git a/apct-tests/perftests/packagemanager/AndroidTest.xml b/apct-tests/perftests/packagemanager/AndroidTest.xml
new file mode 100644
index 000000000000..c112d87d83e1
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/AndroidTest.xml
@@ -0,0 +1,88 @@
+<?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.
+ -->
+<configuration description="Runs PackageManagerPerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="PackageManagerPerfTests.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="force-queryable" value="false" />
+ <option name="test-file-name" value="QueriesAll0.apk" />
+ <option name="test-file-name" value="QueriesAll1.apk" />
+ <option name="test-file-name" value="QueriesAll2.apk" />
+ <option name="test-file-name" value="QueriesAll3.apk" />
+ <option name="test-file-name" value="QueriesAll4.apk" />
+ <option name="test-file-name" value="QueriesAll5.apk" />
+ <option name="test-file-name" value="QueriesAll6.apk" />
+ <option name="test-file-name" value="QueriesAll7.apk" />
+ <option name="test-file-name" value="QueriesAll8.apk" />
+ <option name="test-file-name" value="QueriesAll9.apk" />
+ <option name="test-file-name" value="QueriesAll10.apk" />
+ <option name="test-file-name" value="QueriesAll11.apk" />
+ <option name="test-file-name" value="QueriesAll12.apk" />
+ <option name="test-file-name" value="QueriesAll13.apk" />
+ <option name="test-file-name" value="QueriesAll14.apk" />
+ <option name="test-file-name" value="QueriesAll15.apk" />
+ <option name="test-file-name" value="QueriesAll16.apk" />
+ <option name="test-file-name" value="QueriesAll17.apk" />
+ <option name="test-file-name" value="QueriesAll18.apk" />
+ <option name="test-file-name" value="QueriesAll19.apk" />
+ <option name="test-file-name" value="QueriesAll20.apk" />
+ <option name="test-file-name" value="QueriesAll21.apk" />
+ <option name="test-file-name" value="QueriesAll22.apk" />
+ <option name="test-file-name" value="QueriesAll23.apk" />
+ <option name="test-file-name" value="QueriesAll24.apk" />
+ <option name="test-file-name" value="QueriesAll25.apk" />
+ <option name="test-file-name" value="QueriesAll26.apk" />
+ <option name="test-file-name" value="QueriesAll27.apk" />
+ <option name="test-file-name" value="QueriesAll28.apk" />
+ <option name="test-file-name" value="QueriesAll29.apk" />
+ <option name="test-file-name" value="QueriesAll30.apk" />
+ <option name="test-file-name" value="QueriesAll31.apk" />
+ <option name="test-file-name" value="QueriesAll32.apk" />
+ <option name="test-file-name" value="QueriesAll33.apk" />
+ <option name="test-file-name" value="QueriesAll34.apk" />
+ <option name="test-file-name" value="QueriesAll35.apk" />
+ <option name="test-file-name" value="QueriesAll36.apk" />
+ <option name="test-file-name" value="QueriesAll37.apk" />
+ <option name="test-file-name" value="QueriesAll38.apk" />
+ <option name="test-file-name" value="QueriesAll39.apk" />
+ <option name="test-file-name" value="QueriesAll40.apk" />
+ <option name="test-file-name" value="QueriesAll41.apk" />
+ <option name="test-file-name" value="QueriesAll42.apk" />
+ <option name="test-file-name" value="QueriesAll43.apk" />
+ <option name="test-file-name" value="QueriesAll44.apk" />
+ <option name="test-file-name" value="QueriesAll45.apk" />
+ <option name="test-file-name" value="QueriesAll46.apk" />
+ <option name="test-file-name" value="QueriesAll47.apk" />
+ <option name="test-file-name" value="QueriesAll48.apk" />
+ <option name="test-file-name" value="QueriesAll49.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.perftests.packagemanager" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/data/local/PackageManagerPerfTests" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+</configuration>
diff --git a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
new file mode 100644
index 000000000000..3cb1589bc376
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
@@ -0,0 +1,314 @@
+// 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.
+
+android_test_helper_app {
+ name: "QueriesAll0",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration0",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll1",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration1",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll2",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration2",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll3",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration3",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll4",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration4",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll5",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration5",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll6",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration6",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll7",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration7",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll8",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration8",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll9",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration9",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll10",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration10",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll11",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration11",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll12",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration12",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll13",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration13",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll14",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration14",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll15",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration15",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll16",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration16",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll17",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration17",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll18",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration18",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll19",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration19",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll20",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration20",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll21",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration21",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll22",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration22",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll23",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration23",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll24",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration24",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll25",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration25",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll26",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration26",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll27",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration27",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll28",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration28",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll29",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration29",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll30",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration30",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll31",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration31",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll32",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration32",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll33",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration33",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll34",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration34",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll35",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration35",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll36",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration36",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll37",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration37",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll38",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration38",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll39",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration39",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll40",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration40",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll41",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration41",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll42",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration42",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll43",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration43",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll44",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration44",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll45",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration45",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll46",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration46",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll47",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration47",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll48",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration48",
+ ]
+}
+android_test_helper_app {
+ name: "QueriesAll49",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.appenumeration49",
+ ]
+}
diff --git a/apct-tests/perftests/packagemanager/apps/query-all/AndroidManifest.xml b/apct-tests/perftests/packagemanager/apps/query-all/AndroidManifest.xml
new file mode 100644
index 000000000000..e2cfa0430a32
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/apps/query-all/AndroidManifest.xml
@@ -0,0 +1,81 @@
+<?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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.perftests.appenumeration">
+
+ <application android:hasCode="false" >
+ <activity android:name="android.perftests.utils.PerfTestActivity">
+ <intent-filter>
+ <action android:name="com.android.perftests.packagemanager.PERFTEST" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <queries>
+ <package android:name="com.android.perftests.appenumeration0" />
+ <package android:name="com.android.perftests.appenumeration1" />
+ <package android:name="com.android.perftests.appenumeration2" />
+ <package android:name="com.android.perftests.appenumeration3" />
+ <package android:name="com.android.perftests.appenumeration4" />
+ <package android:name="com.android.perftests.appenumeration5" />
+ <package android:name="com.android.perftests.appenumeration6" />
+ <package android:name="com.android.perftests.appenumeration7" />
+ <package android:name="com.android.perftests.appenumeration8" />
+ <package android:name="com.android.perftests.appenumeration9" />
+ <package android:name="com.android.perftests.appenumeration10" />
+ <package android:name="com.android.perftests.appenumeration11" />
+ <package android:name="com.android.perftests.appenumeration12" />
+ <package android:name="com.android.perftests.appenumeration13" />
+ <package android:name="com.android.perftests.appenumeration14" />
+ <package android:name="com.android.perftests.appenumeration15" />
+ <package android:name="com.android.perftests.appenumeration16" />
+ <package android:name="com.android.perftests.appenumeration17" />
+ <package android:name="com.android.perftests.appenumeration18" />
+ <package android:name="com.android.perftests.appenumeration19" />
+ <package android:name="com.android.perftests.appenumeration20" />
+ <package android:name="com.android.perftests.appenumeration21" />
+ <package android:name="com.android.perftests.appenumeration22" />
+ <package android:name="com.android.perftests.appenumeration23" />
+ <package android:name="com.android.perftests.appenumeration24" />
+ <package android:name="com.android.perftests.appenumeration25" />
+ <package android:name="com.android.perftests.appenumeration26" />
+ <package android:name="com.android.perftests.appenumeration27" />
+ <package android:name="com.android.perftests.appenumeration28" />
+ <package android:name="com.android.perftests.appenumeration29" />
+ <package android:name="com.android.perftests.appenumeration30" />
+ <package android:name="com.android.perftests.appenumeration31" />
+ <package android:name="com.android.perftests.appenumeration32" />
+ <package android:name="com.android.perftests.appenumeration33" />
+ <package android:name="com.android.perftests.appenumeration34" />
+ <package android:name="com.android.perftests.appenumeration35" />
+ <package android:name="com.android.perftests.appenumeration36" />
+ <package android:name="com.android.perftests.appenumeration37" />
+ <package android:name="com.android.perftests.appenumeration38" />
+ <package android:name="com.android.perftests.appenumeration39" />
+ <package android:name="com.android.perftests.appenumeration40" />
+ <package android:name="com.android.perftests.appenumeration41" />
+ <package android:name="com.android.perftests.appenumeration42" />
+ <package android:name="com.android.perftests.appenumeration43" />
+ <package android:name="com.android.perftests.appenumeration44" />
+ <package android:name="com.android.perftests.appenumeration45" />
+ <package android:name="com.android.perftests.appenumeration46" />
+ <package android:name="com.android.perftests.appenumeration47" />
+ <package android:name="com.android.perftests.appenumeration48" />
+ <package android:name="com.android.perftests.appenumeration49" />
+ </queries>
+
+</manifest> \ No newline at end of file
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
new file mode 100644
index 000000000000..d7428cf0ab8a
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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 static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PackageManagerPerfTest {
+ private static final String PERMISSION_NAME_EXISTS =
+ "com.android.perftests.packagemanager.TestPermission";
+ private static final String PERMISSION_NAME_DOESNT_EXIST =
+ "com.android.perftests.packagemanager.TestBadPermission";
+ private static final String OTHER_PACKAGE_NAME = "com.android.perftests.appenumeration0";
+ private static final ComponentName TEST_ACTIVITY =
+ new ComponentName(OTHER_PACKAGE_NAME,
+ "android.perftests.utils.PerfTestActivity");
+
+ @Rule
+ public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Rule
+ public final PlatformCompatChangeRule mPlatformCompatChangeRule =
+ new PlatformCompatChangeRule();
+
+ public PackageManagerPerfTest() throws PackageManager.NameNotFoundException {
+ final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ }
+
+ @Test
+ @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testCheckPermissionExists() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+ final String packageName = TEST_ACTIVITY.getPackageName();
+
+ while (state.keepRunning()) {
+ int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName);
+ }
+ }
+
+ @Test
+ @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testCheckPermissionExistsWithFiltering() {
+ testCheckPermissionExists();
+ }
+
+ @Test
+ @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testCheckPermissionDoesntExist() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+ final String packageName = TEST_ACTIVITY.getPackageName();
+
+ while (state.keepRunning()) {
+ int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName);
+ }
+ }
+
+ @Test
+ @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testCheckPermissionDoesntExistWithFiltering() {
+ testCheckPermissionDoesntExist();
+ }
+
+ @Test
+ @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testQueryIntentActivities() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+ final Intent intent = new Intent("com.android.perftests.core.PERFTEST");
+
+ while (state.keepRunning()) {
+ pm.queryIntentActivities(intent, 0);
+ }
+ }
+
+ @Test
+ @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testQueryIntentActivitiesWithFiltering() {
+ testQueryIntentActivities();
+ }
+
+ @Test
+ @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testGetPackageInfo() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+ while (state.keepRunning()) {
+ pm.getPackageInfo(OTHER_PACKAGE_NAME, 0);
+ }
+ }
+
+ @Test
+ @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testGetPackageInfoWithFiltering() throws Exception {
+ testGetPackageInfo();
+ }
+
+ @Test
+ @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testGetApplicationInfo() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+ while (state.keepRunning()) {
+ pm.getApplicationInfo(OTHER_PACKAGE_NAME, 0);
+ }
+ }
+
+ @Test
+ @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testGetApplicationInfoWithFiltering() throws Exception {
+ testGetApplicationInfo();
+ }
+
+ @Test
+ @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testGetActivityInfo() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+ while (state.keepRunning()) {
+ pm.getActivityInfo(TEST_ACTIVITY, 0);
+ }
+ }
+
+ @Test
+ @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testGetActivityInfoWithFiltering() throws Exception {
+ testGetActivityInfo();
+ }
+
+ @Test
+ @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testGetInstalledPackages() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+ while (state.keepRunning()) {
+ pm.getInstalledPackages(0);
+ }
+ }
+
+ @Test
+ @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+ public void testGetInstalledPackagesWithFiltering() throws Exception {
+ testGetInstalledPackages();
+ }
+}
diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
index bd3b6737f505..f61ea8549236 100644
--- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
+++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
@@ -18,35 +18,60 @@ package android.view.textclassifier;
import android.content.Context;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.SettingsHelper;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
@LargeTest
public class TextClassificationManagerPerfTest {
+ private static final String WRITE_DEVICE_CONFIG_PERMISSION =
+ "android.permission.WRITE_DEVICE_CONFIG";
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ private String mOriginalSystemTextclassifierStatus;
+
+ @BeforeClass
+ public static void setUpClass() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(
+ WRITE_DEVICE_CONFIG_PERMISSION);
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Before
+ public void setUp() {
+ // Saves config original value.
+ mOriginalSystemTextclassifierStatus = DeviceConfig.getProperty(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER, "system_textclassifier_enabled");
+ }
+
@After
public void tearDown() {
- SettingsHelper.delete(
- SettingsHelper.NAMESPACE_GLOBAL, Settings.Global.TEXT_CLASSIFIER_CONSTANTS);
+ // Restores config original value.
+ enableSystemTextclassifier(mOriginalSystemTextclassifierStatus);
}
@Test
public void testGetTextClassifier_systemTextClassifierDisabled() {
Context context = InstrumentationRegistry.getTargetContext();
- SettingsHelper.set(
- SettingsHelper.NAMESPACE_GLOBAL,
- Settings.Global.TEXT_CLASSIFIER_CONSTANTS,
- "system_textclassifier_enabled=false");
+ enableSystemTextclassifier(String.valueOf(false));
TextClassificationManager textClassificationManager =
context.getSystemService(TextClassificationManager.class);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -59,10 +84,7 @@ public class TextClassificationManagerPerfTest {
@Test
public void testGetTextClassifier_systemTextClassifierEnabled() {
Context context = InstrumentationRegistry.getTargetContext();
- SettingsHelper.set(
- SettingsHelper.NAMESPACE_GLOBAL,
- Settings.Global.TEXT_CLASSIFIER_CONSTANTS,
- "system_textclassifier_enabled=true");
+ enableSystemTextclassifier(String.valueOf(true));
TextClassificationManager textClassificationManager =
context.getSystemService(TextClassificationManager.class);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -71,4 +93,9 @@ public class TextClassificationManagerPerfTest {
textClassificationManager.invalidateForTesting();
}
}
+
+ private void enableSystemTextclassifier(String enabled) {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ "system_textclassifier_enabled", enabled, /* makeDefault */ false);
+ }
}
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 69f4748548a7..939164edd13e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -494,9 +494,10 @@ public class JobSchedulerService extends com.android.server.SystemService
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 KEY_MAX_STANDARD_RESCHEDULE_COUNT
+ private static final String DEPRECATED_KEY_MAX_STANDARD_RESCHEDULE_COUNT
= "max_standard_reschedule_count";
- private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_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 =
@@ -525,8 +526,6 @@ public class JobSchedulerService extends com.android.server.SystemService
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 int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
- private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
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 float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
@@ -640,16 +639,6 @@ public class JobSchedulerService extends com.android.server.SystemService
"screen_off_job_concurrency_increase_delay_ms", 30_000);
/**
- * The maximum number of times we allow a job to have itself rescheduled before
- * giving up on it, for standard jobs.
- */
- int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
- /**
- * The maximum number of times we allow a job to have itself rescheduled before
- * giving up on it, for jobs that are executing work.
- */
- int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
- /**
* The minimum backoff time to allow for linear backoff.
*/
long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
@@ -735,10 +724,6 @@ public class JobSchedulerService extends com.android.server.SystemService
SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.parse(mParser);
- MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
- DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
- MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
- DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
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,
@@ -790,8 +775,6 @@ public class JobSchedulerService extends com.android.server.SystemService
SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dump(pw, "");
- pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
- pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
@@ -827,8 +810,6 @@ public class JobSchedulerService extends com.android.server.SystemService
SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dumpProto(proto,
ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
- proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
- proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
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.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
@@ -1390,18 +1371,8 @@ public class JobSchedulerService extends com.android.server.SystemService
// Effective standby bucket can change after this in some situations so use
// the real bucket so that the job is tracked by the controllers.
if (js.getStandbyBucket() == RESTRICTED_INDEX) {
- js.addDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW);
- js.addDynamicConstraint(JobStatus.CONSTRAINT_CHARGING);
- js.addDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY);
- js.addDynamicConstraint(JobStatus.CONSTRAINT_IDLE);
-
mRestrictiveControllers.get(j).startTrackingRestrictedJobLocked(js);
} else {
- js.removeDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW);
- js.removeDynamicConstraint(JobStatus.CONSTRAINT_CHARGING);
- js.removeDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY);
- js.removeDynamicConstraint(JobStatus.CONSTRAINT_IDLE);
-
mRestrictiveControllers.get(j).stopTrackingRestrictedJobLocked(js);
}
}
@@ -1738,19 +1709,6 @@ public class JobSchedulerService extends com.android.server.SystemService
final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
long delayMillis;
- if (failureToReschedule.hasWorkLocked()) {
- if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
- Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
- + backoffAttempts + " > work limit "
- + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
- return null;
- }
- } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
- Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
- + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
- return null;
- }
-
switch (job.getBackoffPolicy()) {
case JobInfo.BACKOFF_POLICY_LINEAR: {
long backoff = initialBackoffMillis;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index f706260edec2..1e89158ca4bb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -17,6 +17,8 @@
package com.android.server.job.controllers;
import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.app.AppGlobals;
@@ -63,26 +65,36 @@ import java.util.function.Predicate;
* @hide
*/
public final class JobStatus {
- static final String TAG = "JobSchedulerService";
+ private static final String TAG = "JobScheduler.JobStatus";
static final boolean DEBUG = JobSchedulerService.DEBUG;
public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
public static final long NO_EARLIEST_RUNTIME = 0L;
- public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
- public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2
- public static final int CONSTRAINT_BATTERY_NOT_LOW =
- JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
+ static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
+ static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2
+ static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3
static final int CONSTRAINT_TIMING_DELAY = 1<<31;
static final int CONSTRAINT_DEADLINE = 1<<30;
- public static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
+ static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
/**
+ * The additional set of dynamic constraints that must be met if the job's effective bucket is
+ * {@link JobSchedulerService#RESTRICTED_INDEX}. Connectivity can be ignored if the job doesn't
+ * need network.
+ */
+ private static final int DYNAMIC_RESTRICTED_CONSTRAINTS =
+ CONSTRAINT_BATTERY_NOT_LOW
+ | CONSTRAINT_CHARGING
+ | CONSTRAINT_CONNECTIVITY
+ | CONSTRAINT_IDLE;
+
+ /**
* The constraints that we want to log to statsd.
*
* Constraints that can be inferred from other atoms have been excluded to avoid logging too
@@ -419,7 +431,11 @@ public final class JobStatus {
this.requiredConstraints = requiredConstraints;
mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
- mReadyDynamicSatisfied = true;
+ if (standbyBucket == RESTRICTED_INDEX) {
+ addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
+ } else {
+ mReadyDynamicSatisfied = true;
+ }
mLastSuccessfulRunTime = lastSuccessfulRunTime;
mLastFailedRunTime = lastFailedRunTime;
@@ -727,6 +743,14 @@ public final class JobStatus {
}
public void setStandbyBucket(int newBucket) {
+ if (newBucket == RESTRICTED_INDEX) {
+ // Adding to the bucket.
+ addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
+ } else if (standbyBucket == RESTRICTED_INDEX) {
+ // Removing from the RESTRICTED bucket.
+ removeDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
+ }
+
standbyBucket = newBucket;
}
@@ -1054,6 +1078,11 @@ public final class JobStatus {
if (old == state) {
return false;
}
+ if (DEBUG) {
+ Slog.v(TAG,
+ "Constraint " + constraint + " is " + (!state ? "NOT " : "") + "satisfied for "
+ + toShortString());
+ }
satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
mReadyDynamicSatisfied =
@@ -1086,38 +1115,40 @@ public final class JobStatus {
}
/**
- * Indicates that this job cannot run without the specified constraint. This is evaluated
+ * Indicates that this job cannot run without the specified constraints. This is evaluated
* separately from the job's explicitly requested constraints and MUST be satisfied before
* the job can run if the app doesn't have quota.
- *
*/
- public void addDynamicConstraint(int constraint) {
- if (constraint == CONSTRAINT_WITHIN_QUOTA) {
+ private void addDynamicConstraints(int constraints) {
+ if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
+ // Quota should never be used as a dynamic constraint.
Slog.wtf(TAG, "Tried to set quota as a dynamic constraint");
- return;
+ constraints &= ~CONSTRAINT_WITHIN_QUOTA;
}
// Connectivity and content trigger are special since they're only valid to add if the
// job has requested network or specific content URIs. Adding these constraints to jobs
// that don't need them doesn't make sense.
- if ((constraint == CONSTRAINT_CONNECTIVITY && !hasConnectivityConstraint())
- || (constraint == CONSTRAINT_CONTENT_TRIGGER && !hasContentTriggerConstraint())) {
- return;
+ if (!hasConnectivityConstraint()) {
+ constraints &= ~CONSTRAINT_CONNECTIVITY;
+ }
+ if (!hasContentTriggerConstraint()) {
+ constraints &= ~CONSTRAINT_CONTENT_TRIGGER;
}
- mDynamicConstraints |= constraint;
+ mDynamicConstraints |= constraints;
mReadyDynamicSatisfied =
mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
}
/**
- * Removes a dynamic constraint from a job, meaning that the requirement is not required for
+ * Removes dynamic constraints from a job, meaning that the requirements are not required for
* the job to run (if the job itself hasn't requested the constraint. This is separate from
* the job's explicitly requested constraints and does not remove those requested constraints.
*
*/
- public void removeDynamicConstraint(int constraint) {
- mDynamicConstraints &= ~constraint;
+ private void removeDynamicConstraints(int constraints) {
+ mDynamicConstraints &= ~constraints;
mReadyDynamicSatisfied =
mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
}
@@ -1193,7 +1224,11 @@ public final class JobStatus {
private boolean isReady(int satisfiedConstraints) {
// Quota and dynamic constraints trump all other constraints.
- if (!mReadyWithinQuota && !mReadyDynamicSatisfied) {
+ // NEVER jobs are not supposed to run at all. Since we're using quota to allow parole
+ // sessions (exempt from dynamic restrictions), we need the additional check to ensure
+ // that NEVER jobs don't run.
+ // TODO: cleanup quota and standby bucket management so we don't need the additional checks
+ if ((!mReadyWithinQuota && !mReadyDynamicSatisfied) || standbyBucket == NEVER_INDEX) {
return false;
}
// Deadline constraint trumps other constraints besides quota and dynamic (except for
diff --git a/apex/sdkextensions/framework/java/android/os/ext/test/Test.java b/apex/sdkextensions/framework/java/android/os/ext/test/Test.java
new file mode 100644
index 000000000000..1715f498be1e
--- /dev/null
+++ b/apex/sdkextensions/framework/java/android/os/ext/test/Test.java
@@ -0,0 +1,53 @@
+/*
+ * 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.ext.test;
+
+import android.annotation.SystemApi;
+
+/**
+ * This class exists temporarily to verify SDK updates are working properly.
+ * @deprecated Do not use.
+ */
+@Deprecated
+public class Test {
+
+ public Test() { }
+
+ /** @hide */
+ public void testA() {}
+
+ /** @hide */
+ public void testB() {}
+
+ /** @hide */
+ public void testC() {}
+
+ /** @hide */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void testD() {}
+
+ public void testE() {}
+
+ /** @hide */
+ @SystemApi
+ public void testF() {}
+
+ /** @hide */
+ @SystemApi
+ public void testG() {}
+
+}
diff --git a/api/current.txt b/api/current.txt
index bdc98146ac7e..fb73f2ac6f84 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2873,7 +2873,7 @@ package android.accessibilityservice {
method public void onSystemActionsChanged();
method public final boolean performGlobalAction(int);
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
- method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>);
+ method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.accessibilityservice.AccessibilityService.ScreenshotResult>);
field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
@@ -2952,6 +2952,12 @@ package android.accessibilityservice {
method public void onMagnificationChanged(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController, @NonNull android.graphics.Region, float, float, float);
}
+ public static final class AccessibilityService.ScreenshotResult {
+ method @Nullable public android.graphics.ColorSpace getColorSpace();
+ method @NonNull public android.hardware.HardwareBuffer getHardwareBuffer();
+ method public long getTimestamp();
+ }
+
public static final class AccessibilityService.SoftKeyboardController {
method public void addOnShowModeChangedListener(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener);
method public void addOnShowModeChangedListener(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener, @Nullable android.os.Handler);
@@ -4009,7 +4015,7 @@ package android.app {
method public android.util.Size getAppTaskThumbnailSize();
method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo();
- method @Nullable public java.util.List<android.app.ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @NonNull public java.util.List<android.app.ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String, @IntRange(from=0) int, @IntRange(from=0) int);
method public int getLargeMemoryClass();
method public int getLauncherLargeIconDensity();
method public int getLauncherLargeIconSize();
@@ -4549,12 +4555,13 @@ package android.app {
method public int getPackageUid();
method public int getPid();
method @NonNull public String getProcessName();
- method public int getPss();
+ method public long getPss();
method public int getRealUid();
method public int getReason();
- method public int getRss();
+ method public long getRss();
method public int getStatus();
method public long getTimestamp();
+ method @NonNull public android.os.UserHandle getUserHandle();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ApplicationExitInfo> CREATOR;
field public static final int REASON_ANR = 6; // 0x6
@@ -6810,7 +6817,7 @@ package android.app.admin {
ctor public DevicePolicyKeyguardService();
method @Nullable public void dismiss();
method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
- method @Nullable public android.view.SurfaceControl onSurfaceReady(@Nullable android.os.IBinder);
+ method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage onSurfaceReady(@Nullable android.os.IBinder);
}
public class DevicePolicyManager {
@@ -29696,7 +29703,7 @@ package android.net {
method public long getReportTimestamp();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR;
- field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttemped";
+ field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted";
field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded";
field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
field public static final int NETWORK_PROBE_DNS = 4; // 0x4
@@ -31289,6 +31296,7 @@ package android.net.wifi {
method public boolean isP2pSupported();
method public boolean isPreferredNetworkOffloadSupported();
method @Deprecated public boolean isScanAlwaysAvailable();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isScanThrottleEnabled();
method public boolean isStaApConcurrencySupported();
method public boolean isTdlsSupported();
method public boolean isWapiSupported();
@@ -31761,7 +31769,7 @@ package android.net.wifi.p2p {
field public static final int GROUP_OWNER_INTENT_MAX = 15; // 0xf
field public static final int GROUP_OWNER_INTENT_MIN = 0; // 0x0
field public String deviceAddress;
- field public int groupOwnerIntent;
+ field @IntRange(from=0, to=15) public int groupOwnerIntent;
field public android.net.wifi.WpsInfo wps;
}
@@ -31953,14 +31961,14 @@ package android.net.wifi.p2p {
method public int getDeviceType();
method public int getMaxThroughput();
method public boolean isContentProtectionSupported();
+ method public boolean isEnabled();
method public boolean isSessionAvailable();
- method public boolean isWfdEnabled();
method public void setContentProtectionSupported(boolean);
- method public void setControlPort(int);
+ method public void setControlPort(@IntRange(from=0) int);
method public boolean setDeviceType(int);
- method public void setMaxThroughput(int);
+ method public void setEnabled(boolean);
+ method public void setMaxThroughput(@IntRange(from=0) int);
method public void setSessionAvailable(boolean);
- method public void setWfdEnabled(boolean);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pWfdInfo> CREATOR;
field public static final int DEVICE_TYPE_PRIMARY_SINK = 1; // 0x1
@@ -37039,6 +37047,15 @@ package android.os {
}
+package android.os.ext.test {
+
+ @Deprecated public class Test {
+ ctor @Deprecated public Test();
+ method @Deprecated public void testE();
+ }
+
+}
+
package android.os.health {
public class HealthStats {
@@ -43988,7 +44005,7 @@ package android.service.quicksettings {
field public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
field public static final String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
field public static final String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
- field public static final String META_DATA_BOOLEAN_TILE = "android.service.quicksettings.BOOLEAN_TILE";
+ field public static final String META_DATA_TOGGLEABLE_TILE = "android.service.quicksettings.TOGGLEABLE_TILE";
}
}
@@ -60828,7 +60845,6 @@ package android.widget {
method public void setTypeface(@Nullable android.graphics.Typeface, int);
method public void setTypeface(@Nullable android.graphics.Typeface);
method public void setWidth(int);
- field public static final int ACCESSIBILITY_ACTION_IME_ENTER = 16908372; // 0x1020054
field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
}
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 90531b16a516..59aa145ce1e9 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1,4 +1,133 @@
// Signature format: 2.0
+package android.net {
+
+ public final class TetheredClient implements android.os.Parcelable {
+ ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
+ method @NonNull public android.net.MacAddress getMacAddress();
+ method public int getTetheringType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
+ }
+
+ public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.LinkAddress getAddress();
+ method @Nullable public String getHostname();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
+ }
+
+ public class TetheringConstants {
+ ctor public TetheringConstants();
+ field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+ field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+ field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+ field public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+ field public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+ }
+
+ public class TetheringManager {
+ ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier<android.os.IBinder>);
+ method public int getLastTetherError(@NonNull String);
+ method @NonNull public String[] getTetherableBluetoothRegexs();
+ method @NonNull public String[] getTetherableIfaces();
+ method @NonNull public String[] getTetherableUsbRegexs();
+ method @NonNull public String[] getTetherableWifiRegexs();
+ method @NonNull public String[] getTetheredIfaces();
+ method @NonNull public String[] getTetheringErroredIfaces();
+ method public boolean isTetheringSupported();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+ method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
+ method @Deprecated public int setUsbTethering(boolean);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+ method @Deprecated public int tether(@NonNull String);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @Deprecated public int untether(@NonNull String);
+ field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+ field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
+ field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+ field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+ field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_ETHERNET = 5; // 0x5
+ field public static final int TETHERING_INVALID = -1; // 0xffffffff
+ field public static final int TETHERING_NCM = 4; // 0x4
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
+ field public static final int TETHERING_WIFI_P2P = 3; // 0x3
+ field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
+ field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9
+ field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8
+ field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
+ field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
+ field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5
+ field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
+ field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
+ field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+ field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
+ field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
+ field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
+ field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+ field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+ field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+ }
+
+ public static interface TetheringManager.OnTetheringEntitlementResultListener {
+ method public void onTetheringEntitlementResult(int);
+ }
+
+ public abstract static class TetheringManager.StartTetheringCallback {
+ ctor public TetheringManager.StartTetheringCallback();
+ method public void onTetheringFailed(int);
+ method public void onTetheringStarted();
+ }
+
+ public abstract static class TetheringManager.TetheringEventCallback {
+ ctor public TetheringManager.TetheringEventCallback();
+ method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+ method public void onError(@NonNull String, int);
+ method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+ method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheringSupported(boolean);
+ method public void onUpstreamChanged(@Nullable android.net.Network);
+ }
+
+ @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
+ ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]);
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+ }
+
+ public static class TetheringManager.TetheringRequest {
+ }
+
+ public static class TetheringManager.TetheringRequest.Builder {
+ ctor public TetheringManager.TetheringRequest.Builder(int);
+ method @NonNull public android.net.TetheringManager.TetheringRequest build();
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+ }
+
+}
+
+package android.os.ext.test {
+
+ @Deprecated public class Test {
+ method @Deprecated public void testD();
+ }
+
+}
+
package android.util {
public final class Log {
diff --git a/api/system-current.txt b/api/system-current.txt
index ff9391d9877d..bf87e55bd017 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -585,10 +585,6 @@ package android.app {
field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
}
- public final class ApplicationExitInfo implements android.os.Parcelable {
- method @NonNull public android.os.UserHandle getUserHandle();
- }
-
public class BroadcastOptions {
method public static android.app.BroadcastOptions makeBasic();
method @RequiresPermission("android.permission.START_ACTIVITIES_FROM_BACKGROUND") public void setBackgroundActivityStartsAllowed(boolean);
@@ -2212,6 +2208,7 @@ package android.content.pm {
field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
+ field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000
@@ -3704,8 +3701,8 @@ package android.hardware.soundtrigger {
public static final class SoundTrigger.ModuleProperties implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
- field public static final int CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
- field public static final int CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
+ field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
+ field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> CREATOR;
field public final int audioCapabilities;
field @NonNull public final String description;
@@ -4215,15 +4212,15 @@ package android.media {
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioAttributes.Builder setSystemUsage(int);
}
- public final class AudioDeviceAddress implements android.os.Parcelable {
- ctor public AudioDeviceAddress(@NonNull android.media.AudioDeviceInfo);
- ctor public AudioDeviceAddress(int, int, @NonNull String);
+ public final class AudioDevice implements android.os.Parcelable {
+ ctor public AudioDevice(@NonNull android.media.AudioDeviceInfo);
+ ctor public AudioDevice(int, int, @NonNull String);
method public int describeContents();
method @NonNull public String getAddress();
method public int getRole();
method public int getType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioDeviceAddress> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioDevice> CREATOR;
field public static final int ROLE_INPUT = 1; // 0x1
field public static final int ROLE_OUTPUT = 2; // 0x2
}
@@ -4260,11 +4257,11 @@ package android.media {
method @IntRange(from=0) public int getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
- method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAddress> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDevice> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) public int getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
- method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAddress getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
+ method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDevice getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method public boolean isAudioServerRunning();
@@ -4278,7 +4275,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) int);
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAddress);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDevice);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -4374,7 +4371,7 @@ package android.media {
package android.media.audiofx {
public class AudioEffect {
- ctor @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public AudioEffect(@NonNull java.util.UUID, @NonNull android.media.AudioDeviceAddress);
+ ctor @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public AudioEffect(@NonNull java.util.UUID, @NonNull android.media.AudioDevice);
}
}
@@ -7692,6 +7689,7 @@ package android.net.wifi {
method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanThrottleEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setVerboseLoggingEnabled(boolean);
method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
@@ -7836,23 +7834,23 @@ package android.net.wifi {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int);
}
- public final class WifiOemConfigStoreMigrationHook {
- method @Nullable public static android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData load();
+ public final class WifiOemMigrationHook {
+ method @Nullable public static android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData loadFromConfigStore();
}
- public static final class WifiOemConfigStoreMigrationHook.MigrationData implements android.os.Parcelable {
+ public static final class WifiOemMigrationHook.ConfigStoreMigrationData implements android.os.Parcelable {
method public int describeContents();
method @Nullable public java.util.List<android.net.wifi.WifiConfiguration> getUserSavedNetworkConfigurations();
method @Nullable public android.net.wifi.SoftApConfiguration getUserSoftApConfiguration();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData> CREATOR;
}
- public static final class WifiOemConfigStoreMigrationHook.MigrationData.Builder {
- ctor public WifiOemConfigStoreMigrationHook.MigrationData.Builder();
- method @NonNull public android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData build();
- method @NonNull public android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
- method @NonNull public android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
+ public static final class WifiOemMigrationHook.ConfigStoreMigrationData.Builder {
+ ctor public WifiOemMigrationHook.ConfigStoreMigrationData.Builder();
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData build();
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
}
public class WifiScanner {
@@ -8110,7 +8108,7 @@ package android.net.wifi.p2p {
public final class WifiP2pGroupList implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public java.util.Collection<android.net.wifi.p2p.WifiP2pGroup> getGroupList();
+ method @NonNull public java.util.List<android.net.wifi.p2p.WifiP2pGroup> getGroupList();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pGroupList> CREATOR;
}
@@ -8118,12 +8116,13 @@ package android.net.wifi.p2p {
public class WifiP2pManager {
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void deletePersistentGroup(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
- method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void listen(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, boolean, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public void requestPersistentGroupInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setDeviceName(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull String, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
method @RequiresPermission(allOf={android.Manifest.permission.CONNECTIVITY_INTERNAL, android.Manifest.permission.CONFIGURE_WIFI_DISPLAY}) public void setMiracastMode(int);
method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setWfdInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pWfdInfo, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setWifiP2pChannels(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void startListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void stopListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
field public static final String ACTION_WIFI_P2P_PERSISTENT_GROUPS_CHANGED = "android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED";
field public static final int MIRACAST_DISABLED = 0; // 0x0
field public static final int MIRACAST_SINK = 2; // 0x2
@@ -9143,6 +9142,15 @@ package android.os.ext {
}
+package android.os.ext.test {
+
+ @Deprecated public class Test {
+ method @Deprecated public void testF();
+ method @Deprecated public void testG();
+ }
+
+}
+
package android.os.image {
public class DynamicSystemClient {
@@ -12423,7 +12431,6 @@ package android.telephony {
field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
field public static final int CHANGE_ICC_LOCK_SUCCESS = 2147483647; // 0x7fffffff
- field public static final int DEFAULT_PREFERRED_NETWORK_MODE = 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 @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto";
@@ -14129,6 +14136,10 @@ package android.view.contentcapture {
method public boolean isContentCaptureFeatureEnabled();
}
+ public abstract class ContentCaptureSession implements java.lang.AutoCloseable {
+ field public static final int NO_SESSION_ID = 0; // 0x0
+ }
+
public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
method @Nullable public android.view.autofill.AutofillId getParentAutofillId();
}
diff --git a/api/test-current.txt b/api/test-current.txt
index e1f83822609d..826c27546aad 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2539,6 +2539,7 @@ package android.os {
}
public class UserManager {
+ method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS"}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
method public static boolean isSplitSystemUser();
field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
}
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index de1dbc90eb2d..c643b0e8800c 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -37,6 +37,10 @@ typedef uint16_t EntryId; // eeee in 0xpptteeee
namespace utils {
+// Returns whether the Res_value::data_type represents a dynamic or regular resource reference.
+bool IsReference(uint8_t data_type);
+
+// Converts the Res_value::data_type to a human-readable string representation.
StringPiece DataTypeToString(uint8_t data_type);
struct OverlayManifestInfo {
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 407478945151..43cfec3f9cf9 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -27,6 +27,7 @@
#include "idmap2/ResourceUtils.h"
using android::base::StringPrintf;
+using android::idmap2::utils::IsReference;
using android::idmap2::utils::ResToTypeEntryName;
namespace android::idmap2 {
@@ -200,8 +201,7 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManage
// Only rewrite resources defined within the overlay package to their corresponding target
// resource ids at runtime.
bool rewrite_overlay_reference =
- (overlay_resource->dataType == Res_value::TYPE_REFERENCE ||
- overlay_resource->dataType == Res_value::TYPE_DYNAMIC_REFERENCE)
+ IsReference(overlay_resource->dataType)
? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data)
: false;
@@ -331,8 +331,13 @@ Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_a
std::unique_ptr<uint8_t[]> string_pool_data;
Result<ResourceMapping> resource_mapping = {{}};
if (overlay_info.resource_mapping != 0U) {
+ // Use the dynamic reference table to find the assigned resource id of the map xml.
+ const auto& ref_table = overlay_asset_manager.GetDynamicRefTableForCookie(0);
+ uint32_t resource_mapping_id = overlay_info.resource_mapping;
+ ref_table->lookupResourceId(&resource_mapping_id);
+
// Load the overlay resource mappings from the file specified using android:resourcesMap.
- auto asset = OpenNonAssetFromResource(overlay_info.resource_mapping, overlay_asset_manager);
+ auto asset = OpenNonAssetFromResource(resource_mapping_id, overlay_asset_manager);
if (!asset) {
return Error("failed opening xml for android:resourcesMap: %s",
asset.GetErrorMessage().c_str());
@@ -404,8 +409,7 @@ Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
- if (rewrite_overlay_reference &&
- (data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE)) {
+ if (rewrite_overlay_reference && IsReference(data_type)) {
overlay_map_.insert(std::make_pair(data_value, target_resource));
}
@@ -421,8 +425,7 @@ void ResourceMapping::RemoveMapping(ResourceId target_resource) {
const TargetValue value = target_iter->second;
target_map_.erase(target_iter);
- if (value.data_type != Res_value::TYPE_REFERENCE &&
- value.data_type != Res_value::TYPE_DYNAMIC_REFERENCE) {
+ if (!IsReference(value.data_type)) {
return;
}
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index a5df746ca733..98d026bc70dc 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -33,6 +33,10 @@ using android::util::Utf16ToUtf8;
namespace android::idmap2::utils {
+bool IsReference(uint8_t data_type) {
+ return data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE;
+}
+
StringPiece DataTypeToString(uint8_t data_type) {
switch (data_type) {
case Res_value::TYPE_NULL:
@@ -133,7 +137,7 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
}
if (auto result_value = overlay_it->GetAttributeValue("resourcesMap")) {
- if ((*result_value).dataType == Res_value::TYPE_REFERENCE) {
+ if (IsReference((*result_value).dataType)) {
info.resource_mapping = (*result_value).data;
} else {
return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index f55acee029dc..8af4037be954 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -56,12 +56,12 @@ TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
});
ASSERT_THAT(v, NotNull());
- ASSERT_EQ(v->size(), 10U);
+ ASSERT_EQ(v->size(), 11U);
ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
std::set<std::string>(
{root + "/target/target.apk", root + "/target/target-no-overlayable.apk",
root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk",
- root + "/overlay/overlay-no-name-static.apk",
+ root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-shared.apk",
root + "/overlay/overlay-static-1.apk", root + "/overlay/overlay-static-2.apk",
root + "/signature-overlay/signature-overlay.apk",
root + "/system-overlay/system-overlay.apk",
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 4bc625565144..a2c156063757 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -247,6 +247,43 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
ASSERT_OVERLAY_ENTRY(overlay_entries[3], 0x7f020002, 0x7f02000f);
}
+TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
+ std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
+ std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-shared.apk";
+
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
+ auto& idmap = *idmap_result;
+ ASSERT_THAT(idmap, NotNull());
+
+ const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+ ASSERT_EQ(dataBlocks.size(), 1U);
+
+ const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ ASSERT_THAT(data, NotNull());
+
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00010000);
+ ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020000);
+ ASSERT_TARGET_ENTRY(target_entries[2], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020001);
+ ASSERT_TARGET_ENTRY(target_entries[3], 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020002);
+
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x00010000, 0x7f010000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x00020000, 0x7f02000c);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x00020001, 0x7f02000e);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[3], 0x00020002, 0x7f02000f);
+}
+
TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
OverlayManifestInfo info{};
info.target_package = "test.target";
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
index b921b0d3d3ad..114b099598fa 100755
--- a/cmds/idmap2/tests/data/overlay/build
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -51,4 +51,12 @@ aapt2 link \
-o overlay-static-2.apk \
compiled.flata
+aapt2 link \
+ --no-resource-removal \
+ --shared-lib \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest AndroidManifest.xml \
+ -o overlay-shared.apk \
+ compiled.flata
+
rm compiled.flata
diff --git a/cmds/idmap2/tests/data/overlay/overlay-shared.apk b/cmds/idmap2/tests/data/overlay/overlay-shared.apk
new file mode 100644
index 000000000000..93dcc82f9358
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-shared.apk
Binary files differ
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 3a2472efb601..70f309add5df 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -118,22 +118,20 @@ cc_defaults {
],
static_libs: [
- "android.frameworks.stats@1.0",
"libbase",
"libcutils",
"libprotoutil",
"libstatslog",
"libstatsmetadata",
"libsysutils",
+ "libutils",
],
shared_libs: [
"libbinder",
- "libhidlbase",
"libincident",
"liblog",
"libservices",
"libstatssocket",
- "libutils",
],
}
@@ -222,8 +220,6 @@ cc_binary {
shared_libs: ["libgtest_prod"],
- vintf_fragments: ["android.frameworks.stats@1.0-service.xml"],
-
init_rc: ["statsd.rc"],
}
diff --git a/cmds/statsd/android.frameworks.stats@1.0-service.xml b/cmds/statsd/android.frameworks.stats@1.0-service.xml
deleted file mode 100644
index bb02f66a28b1..000000000000
--- a/cmds/statsd/android.frameworks.stats@1.0-service.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="framework">
- <hal>
- <name>android.frameworks.stats</name>
- <transport>hwbinder</transport>
- <version>1.0</version>
- <interface>
- <name>IStats</name>
- <instance>default</instance>
- </interface>
- </hal>
-</manifest>
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 0256e3617dd7..a06e59c8e409 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1354,7 +1354,6 @@ Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackType
return Status::ok();
}
-
Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) {
ENFORCE_UID(AID_SYSTEM);
// TODO: add verifier permission
@@ -1372,103 +1371,6 @@ Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experiment
return Status::ok();
}
-hardware::Return<void> StatsService::reportSpeakerImpedance(
- const SpeakerImpedance& speakerImpedance) {
- android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED,
- speakerImpedance.speakerLocation, speakerImpedance.milliOhms);
-
- return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportHardwareFailed(const HardwareFailed& hardwareFailed) {
- android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType),
- hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
-
- return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportPhysicalDropDetected(
- const PhysicalDropDetected& physicalDropDetected) {
- android::util::stats_write(android::util::PHYSICAL_DROP_DETECTED,
- int32_t(physicalDropDetected.confidencePctg), physicalDropDetected.accelPeak,
- physicalDropDetected.freefallDuration);
-
- return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportChargeCycles(const ChargeCycles& chargeCycles) {
- std::vector<int32_t> buckets = chargeCycles.cycleBucket;
- int initialSize = buckets.size();
- for (int i = 0; i < 10 - initialSize; i++) {
- buckets.push_back(-1); // Push -1 for buckets that do not exist.
- }
- android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1],
- buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8],
- buckets[9]);
-
- return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportBatteryHealthSnapshot(
- const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
- android::util::stats_write(android::util::BATTERY_HEALTH_SNAPSHOT,
- int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC,
- batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA,
- batteryHealthSnapshotArgs.openCircuitVoltageMicroV,
- batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent);
-
- return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportSlowIo(const SlowIo& slowIo) {
- android::util::stats_write(android::util::SLOW_IO, int32_t(slowIo.operation), slowIo.count);
-
- return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportBatteryCausedShutdown(
- const BatteryCausedShutdown& batteryCausedShutdown) {
- android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN,
- batteryCausedShutdown.voltageMicroV);
-
- return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportUsbPortOverheatEvent(
- const UsbPortOverheatEvent& usbPortOverheatEvent) {
- android::util::stats_write(android::util::USB_PORT_OVERHEAT_EVENT_REPORTED,
- usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC,
- usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis,
- usbPortOverheatEvent.timeToInactive);
-
- return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportSpeechDspStat(
- const SpeechDspStat& speechDspStat) {
- android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED,
- speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis,
- speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount);
-
- return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportVendorAtom(const VendorAtom& vendorAtom) {
- std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
- if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
- ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
- return hardware::Void();
- }
- if (reverseDomainName.length() > 50) {
- ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
- return hardware::Void();
- }
- LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), vendorAtom);
- mProcessor->OnLogEvent(&event);
-
- return hardware::Void();
-}
-
void StatsService::binderDied(const wp <IBinder>& who) {
ALOGW("statscompanion service died");
StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index af3016f86773..82a5a5305df4 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -27,8 +27,6 @@
#include "shell/ShellSubscriber.h"
#include "statscompanion_util.h"
-#include <android/frameworks/stats/1.0/IStats.h>
-#include <android/frameworks/stats/1.0/types.h>
#include <android/os/BnStatsd.h>
#include <android/os/IPendingIntentRef.h>
#include <android/os/IStatsCompanionService.h>
@@ -41,7 +39,6 @@
using namespace android;
using namespace android::binder;
-using namespace android::frameworks::stats::V1_0;
using namespace android::os;
using namespace std;
@@ -49,10 +46,7 @@ namespace android {
namespace os {
namespace statsd {
-using android::hardware::Return;
-
class StatsService : public BnStatsd,
- public IStats,
public IBinder::DeathRecipient {
public:
StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue);
@@ -207,61 +201,6 @@ public:
*/
virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut);
- /**
- * Binder call to get SpeakerImpedance atom.
- */
- virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override;
-
- /**
- * Binder call to get HardwareFailed atom.
- */
- virtual Return<void> reportHardwareFailed(const HardwareFailed& hardwareFailed) override;
-
- /**
- * Binder call to get PhysicalDropDetected atom.
- */
- virtual Return<void> reportPhysicalDropDetected(
- const PhysicalDropDetected& physicalDropDetected) override;
-
- /**
- * Binder call to get ChargeCyclesReported atom.
- */
- virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override;
-
- /**
- * Binder call to get BatteryHealthSnapshot atom.
- */
- virtual Return<void> reportBatteryHealthSnapshot(
- const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) override;
-
- /**
- * Binder call to get SlowIo atom.
- */
- virtual Return<void> reportSlowIo(const SlowIo& slowIo) override;
-
- /**
- * Binder call to get BatteryCausedShutdown atom.
- */
- virtual Return<void> reportBatteryCausedShutdown(
- const BatteryCausedShutdown& batteryCausedShutdown) override;
-
- /**
- * Binder call to get UsbPortOverheatEvent atom.
- */
- virtual Return<void> reportUsbPortOverheatEvent(
- const UsbPortOverheatEvent& usbPortOverheatEvent) override;
-
- /**
- * Binder call to get Speech DSP state atom.
- */
- virtual Return<void> reportSpeechDspStat(
- const SpeechDspStat& speechDspStat) override;
-
- /**
- * Binder call to get vendor atom.
- */
- virtual Return<void> reportVendorAtom(const VendorAtom& vendorAtom) override;
-
/** IBinder::DeathRecipient */
virtual void binderDied(const wp<IBinder>& who) override;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index e8fc603088a1..103eb0c78bd5 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -195,37 +195,6 @@ LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requi
}
LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
- const VendorAtom& vendorAtom) {
- mLogdTimestampNs = wallClockTimestampNs;
- mElapsedTimestampNs = elapsedTimestampNs;
- mTagId = vendorAtom.atomId;
- mLogUid = AID_STATSD;
-
- mValues.push_back(
- FieldValue(Field(mTagId, getSimpleField(1)), Value(vendorAtom.reverseDomainName)));
- for (int i = 0; i < (int)vendorAtom.values.size(); i++) {
- switch (vendorAtom.values[i].getDiscriminator()) {
- case VendorAtom::Value::hidl_discriminator::intValue:
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
- Value(vendorAtom.values[i].intValue())));
- break;
- case VendorAtom::Value::hidl_discriminator::longValue:
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
- Value(vendorAtom.values[i].longValue())));
- break;
- case VendorAtom::Value::hidl_discriminator::floatValue:
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
- Value(vendorAtom.values[i].floatValue())));
- break;
- case VendorAtom::Value::hidl_discriminator::stringValue:
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
- Value(vendorAtom.values[i].stringValue())));
- break;
- }
- }
-}
-
-LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const InstallTrainInfo& trainInfo) {
mLogdTimestampNs = wallClockTimestampNs;
mElapsedTimestampNs = elapsedTimestampNs;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index e4b784e069ca..5509c093a6f6 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,7 +18,6 @@
#include "FieldValue.h"
-#include <android/frameworks/stats/1.0/types.h>
#include <android/util/ProtoOutputStream.h>
#include <private/android_logger.h>
#include <stats_event_list.h>
@@ -27,8 +26,6 @@
#include <string>
#include <vector>
-using namespace android::frameworks::stats::V1_0;
-
namespace android {
namespace os {
namespace statsd {
@@ -103,9 +100,6 @@ public:
const std::vector<uint8_t>& experimentIds, int32_t userId);
explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
- const VendorAtom& vendorAtom);
-
- explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const InstallTrainInfo& installTrainInfo);
~LogEvent();
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 58bfeb337da4..140ef4e9cea9 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -23,7 +23,6 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
-#include <hidl/HidlTransportSupport.h>
#include <utils/Looper.h>
#include <stdio.h>
@@ -75,8 +74,6 @@ int main(int /*argc*/, char** /*argv*/) {
ps->giveThreadPoolName();
IPCThreadState::self()->disableBackgroundScheduling(true);
- ::android::hardware::configureRpcThreadpool(4 /*threads*/, false /*willJoin*/);
-
std::shared_ptr<LogEventQueue> eventQueue =
std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/);
@@ -89,12 +86,6 @@ int main(int /*argc*/, char** /*argv*/) {
return -1;
}
- auto ret = gStatsService->registerAsService();
- if (ret != ::android::OK) {
- ALOGE("Failed to add service as HIDL service");
- return 1; // or handle error
- }
-
registerSigHandler();
gStatsService->sayHiToStatsCompanion();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 11f7f465cca5..b65f68e177ca 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -28,7 +28,9 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
import android.graphics.Region;
+import android.hardware.HardwareBuffer;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -585,7 +587,12 @@ public abstract class AccessibilityService extends Service {
private FingerprintGestureController mFingerprintGestureController;
/** @hide */
- public static final String KEY_ACCESSIBILITY_SCREENSHOT = "screenshot";
+ public static final String KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER =
+ "screenshot_hardwareBuffer";
+
+ /** @hide */
+ public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID =
+ "screenshot_colorSpaceId";
/**
* Callback for {@link android.view.accessibility.AccessibilityEvent}s.
@@ -1888,8 +1895,9 @@ public abstract class AccessibilityService extends Service {
}
/**
- * Takes a screenshot of the specified display and returns it by {@link Bitmap.Config#HARDWARE}
- * format.
+ * Takes a screenshot of the specified display and returns it via an
+ * {@link AccessibilityService.ScreenshotResult}. You can use {@link Bitmap#wrapHardwareBuffer}
+ * to construct the bitmap from the ScreenshotResult's payload.
* <p>
* <strong>Note:</strong> In order to take screenshot your service has
* to declare the capability to take screenshot by setting the
@@ -1907,7 +1915,7 @@ public abstract class AccessibilityService extends Service {
* @return {@code true} if the taking screenshot accepted, {@code false} if not.
*/
public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
- @NonNull Consumer<Bitmap> callback) {
+ @NonNull Consumer<ScreenshotResult> callback) {
Preconditions.checkNotNull(executor, "executor cannot be null");
Preconditions.checkNotNull(callback, "callback cannot be null");
final IAccessibilityServiceConnection connection =
@@ -1917,14 +1925,22 @@ public abstract class AccessibilityService extends Service {
return false;
}
try {
- connection.takeScreenshotWithCallback(displayId, new RemoteCallback((result) -> {
- final Bitmap screenshot = result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT);
- final long identity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> callback.accept(screenshot));
- } finally {
- Binder.restoreCallingIdentity(identity);
+ connection.takeScreenshot(displayId, new RemoteCallback((result) -> {
+ if (result == null) {
+ sendScreenshotResult(executor, callback, null);
+ return;
}
+ final HardwareBuffer hardwareBuffer =
+ result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER);
+ final int colorSpaceId =
+ result.getInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID);
+ ColorSpace colorSpace = null;
+ if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
+ colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+ }
+ ScreenshotResult screenshot = new ScreenshotResult(hardwareBuffer,
+ colorSpace, System.currentTimeMillis());
+ sendScreenshotResult(executor, callback, screenshot);
}));
} catch (RemoteException re) {
throw new RuntimeException(re);
@@ -2323,4 +2339,67 @@ public abstract class AccessibilityService extends Service {
this.handler = handler;
}
}
+
+ private void sendScreenshotResult(Executor executor, Consumer<ScreenshotResult> callback,
+ ScreenshotResult screenshot) {
+ final ScreenshotResult result = screenshot;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.accept(result));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Class including hardwareBuffer, colorSpace, and timestamp to be the result for
+ * {@link AccessibilityService#takeScreenshot} API.
+ * <p>
+ * <strong>Note:</strong> colorSpace would be null if the name of this colorSpace isn't at
+ * {@link ColorSpace.Named}.
+ * </p>
+ */
+ public static final class ScreenshotResult {
+ private final @NonNull HardwareBuffer mHardwareBuffer;
+ private final @Nullable ColorSpace mColorSpace;
+ private final long mTimestamp;
+
+ private ScreenshotResult(@NonNull HardwareBuffer hardwareBuffer,
+ @Nullable ColorSpace colorSpace, long timestamp) {
+ Preconditions.checkNotNull(hardwareBuffer, "hardwareBuffer cannot be null");
+ mHardwareBuffer = hardwareBuffer;
+ mColorSpace = colorSpace;
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * Gets the colorSpace identifying a specific organization of colors of the screenshot.
+ *
+ * @return the colorSpace or {@code null} if the name of colorSpace isn't at
+ * {@link ColorSpace.Named}
+ */
+ @Nullable
+ public ColorSpace getColorSpace() {
+ return mColorSpace;
+ }
+
+ /**
+ * Gets the hardwareBuffer representing a memory buffer of the screenshot.
+ *
+ * @return the hardwareBuffer
+ */
+ @NonNull
+ public HardwareBuffer getHardwareBuffer() {
+ return mHardwareBuffer;
+ }
+
+ /**
+ * Gets the timestamp of taking the screenshot.
+ *
+ * @return the timestamp from {@link System#currentTimeMillis()}
+ */
+ public long getTimestamp() {
+ return mTimestamp;
+ };
+ }
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 5db4dd7470d8..9177d4d27491 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -110,7 +110,5 @@ interface IAccessibilityServiceConnection {
int getWindowIdForLeashToken(IBinder token);
- Bitmap takeScreenshot(int displayId);
-
- void takeScreenshotWithCallback(int displayId, in RemoteCallback callback);
+ void takeScreenshot(int displayId, in RemoteCallback callback);
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index db9aa18dbd5a..7ee44053d4d5 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -94,6 +94,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.List;
import java.util.Locale;
@@ -3555,13 +3556,13 @@ public class ActivityManager {
* @return a list of {@link ApplicationExitInfo} records matching the criteria, sorted in
* the order from most recent to least recent.
*/
- @Nullable
+ @NonNull
public List<ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String packageName,
@IntRange(from = 0) int pid, @IntRange(from = 0) int maxNum) {
try {
ParceledListSlice<ApplicationExitInfo> r = getService().getHistoricalProcessExitReasons(
packageName, pid, maxNum, mContext.getUserId());
- return r == null ? null : r.getList();
+ return r == null ? Collections.emptyList() : r.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 4bf5f07e86be..c55453e94960 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -19,7 +19,6 @@ package android.app;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.app.ActivityManager.RunningAppProcessInfo.Importance;
import android.icu.text.SimpleDateFormat;
import android.os.Parcel;
@@ -245,12 +244,12 @@ public final class ApplicationExitInfo implements Parcelable {
/**
* @see {@link #getPss}
*/
- private int mPss;
+ private long mPss;
/**
* @see {@link #getRss}
*/
- private int mRss;
+ private long mRss;
/**
* @see {@link #getTimestamp}
@@ -385,7 +384,7 @@ public final class ApplicationExitInfo implements Parcelable {
* it's NOT the exact memory information prior to its death; and it'll be zero
* if the process died before system had a chance to take the sample. </p>
*/
- public int getPss() {
+ public long getPss() {
return mPss;
}
@@ -396,12 +395,13 @@ public final class ApplicationExitInfo implements Parcelable {
* it's NOT the exact memory information prior to its death; and it'll be zero
* if the process died before system had a chance to take the sample. </p>
*/
- public int getRss() {
+ public long getRss() {
return mRss;
}
/**
- * The timestamp of the process's death, in milliseconds since the epoch.
+ * The timestamp of the process's death, in milliseconds since the epoch,
+ * as returned by {@link System#currentTimeMillis System.currentTimeMillis()}.
*/
public long getTimestamp() {
return mTimestamp;
@@ -409,6 +409,9 @@ public final class ApplicationExitInfo implements Parcelable {
/**
* The human readable description of the process's death, given by the system; could be null.
+ *
+ * <p class="note">Note: only intended to be human-readable and the system provides no
+ * guarantees that the format is stable across devices or Android releases.</p>
*/
public @Nullable String getDescription() {
return mDescription;
@@ -416,10 +419,7 @@ public final class ApplicationExitInfo implements Parcelable {
/**
* Return the user id of the record on a multi-user system.
- *
- * @hide
*/
- @SystemApi
public @NonNull UserHandle getUserHandle() {
return UserHandle.of(UserHandle.getUserId(mRealUid));
}
@@ -546,7 +546,7 @@ public final class ApplicationExitInfo implements Parcelable {
*
* @hide
*/
- public void setPss(final int pss) {
+ public void setPss(final long pss) {
mPss = pss;
}
@@ -555,7 +555,7 @@ public final class ApplicationExitInfo implements Parcelable {
*
* @hide
*/
- public void setRss(final int rss) {
+ public void setRss(final long rss) {
mRss = rss;
}
@@ -630,8 +630,8 @@ public final class ApplicationExitInfo implements Parcelable {
dest.writeInt(mSubReason);
dest.writeInt(mStatus);
dest.writeInt(mImportance);
- dest.writeInt(mPss);
- dest.writeInt(mRss);
+ dest.writeLong(mPss);
+ dest.writeLong(mRss);
dest.writeLong(mTimestamp);
dest.writeString(mDescription);
}
@@ -669,8 +669,8 @@ public final class ApplicationExitInfo implements Parcelable {
mSubReason = in.readInt();
mStatus = in.readInt();
mImportance = in.readInt();
- mPss = in.readInt();
- mRss = in.readInt();
+ mPss = in.readLong();
+ mRss = in.readLong();
mTimestamp = in.readLong();
mDescription = in.readString();
}
@@ -848,10 +848,10 @@ public final class ApplicationExitInfo implements Parcelable {
mImportance = proto.readInt(ApplicationExitInfoProto.IMPORTANCE);
break;
case (int) ApplicationExitInfoProto.PSS:
- mPss = proto.readInt(ApplicationExitInfoProto.PSS);
+ mPss = proto.readLong(ApplicationExitInfoProto.PSS);
break;
case (int) ApplicationExitInfoProto.RSS:
- mRss = proto.readInt(ApplicationExitInfoProto.RSS);
+ mRss = proto.readLong(ApplicationExitInfoProto.RSS);
break;
case (int) ApplicationExitInfoProto.TIMESTAMP:
mTimestamp = proto.readLong(ApplicationExitInfoProto.TIMESTAMP);
@@ -891,8 +891,8 @@ public final class ApplicationExitInfo implements Parcelable {
result = 31 * result + mSubReason;
result = 31 * result + mImportance;
result = 31 * result + mStatus;
- result = 31 * result + mPss;
- result = 31 * result + mRss;
+ result = 31 * result + (int) mPss;
+ result = 31 * result + (int) mRss;
result = 31 * result + Long.hashCode(mTimestamp);
result = 31 * result + Objects.hashCode(mProcessName);
result = 31 * result + Objects.hashCode(mDescription);
diff --git a/core/java/android/app/ITaskOrganizerController.aidl b/core/java/android/app/ITaskOrganizerController.aidl
index bfc42ef8848d..5d5956e4dca4 100644
--- a/core/java/android/app/ITaskOrganizerController.aidl
+++ b/core/java/android/app/ITaskOrganizerController.aidl
@@ -51,6 +51,9 @@ interface ITaskOrganizerController {
/** Deletes a persistent root task in WM */
boolean deleteRootTask(IWindowContainer task);
+ /** Gets direct child tasks (ordered from top-to-bottom) */
+ List<ActivityManager.RunningTaskInfo> getChildTasks(in IWindowContainer parent);
+
/** Get the root task which contains the current ime target */
IWindowContainer getImeTarget(int display);
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
index 36ae450d342b..d279983a5793 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -60,12 +60,12 @@ public class WindowContext extends ContextWrapper {
if (token != null && !isWindowToken(token)) {
throw new IllegalArgumentException("Token must be registered to server.");
}
+ mToken = token != null ? token : new Binder();
- final ContextImpl contextImpl = createBaseWindowContext(base, token);
+ final ContextImpl contextImpl = createBaseWindowContext(base, mToken);
attachBaseContext(contextImpl);
contextImpl.setOuterContext(this);
- mToken = token != null ? token : new Binder();
mDisplayId = getDisplayId();
mWindowManager = new WindowManagerImpl(this);
mWindowManager.setDefaultToken(mToken);
diff --git a/core/java/android/app/admin/DevicePolicyKeyguardService.java b/core/java/android/app/admin/DevicePolicyKeyguardService.java
index c2a76c5014c0..2ac5ebfc7c84 100644
--- a/core/java/android/app/admin/DevicePolicyKeyguardService.java
+++ b/core/java/android/app/admin/DevicePolicyKeyguardService.java
@@ -22,7 +22,7 @@ import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
-import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
/**
* Client interface for providing the SystemUI with secondary lockscreen information.
@@ -43,14 +43,14 @@ public class DevicePolicyKeyguardService extends Service {
@Override
public void onSurfaceReady(@Nullable IBinder hostInputToken, IKeyguardCallback callback) {
mCallback = callback;
- SurfaceControl surfaceControl =
+ SurfaceControlViewHost.SurfacePackage surfacePackage =
DevicePolicyKeyguardService.this.onSurfaceReady(hostInputToken);
if (mCallback != null) {
try {
- mCallback.onSurfaceControlCreated(surfaceControl);
+ mCallback.onRemoteContentReady(surfacePackage);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to return created SurfaceControl", e);
+ Log.e(TAG, "Failed to return created SurfacePackage", e);
}
}
}
@@ -65,11 +65,11 @@ public class DevicePolicyKeyguardService extends Service {
/**
* Called by keyguard once the host surface for the secondary lockscreen is ready to display
* remote content.
- * @return the {@link SurfaceControl} for the Surface the secondary lockscreen content is
- * attached to.
+ * @return the {@link SurfaceControlViewHost.SurfacePackage} for the Surface the
+ * secondary lockscreen content is attached to.
*/
@Nullable
- public SurfaceControl onSurfaceReady(@Nullable IBinder hostInputToken) {
+ public SurfaceControlViewHost.SurfacePackage onSurfaceReady(@Nullable IBinder hostInputToken) {
return null;
}
diff --git a/core/java/android/app/admin/IKeyguardCallback.aidl b/core/java/android/app/admin/IKeyguardCallback.aidl
index 81e7d4dee902..856033dc8129 100644
--- a/core/java/android/app/admin/IKeyguardCallback.aidl
+++ b/core/java/android/app/admin/IKeyguardCallback.aidl
@@ -15,13 +15,13 @@
*/
package android.app.admin;
-import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
/**
* Internal IPC interface for informing the keyguard of events on the secondary lockscreen.
* @hide
*/
interface IKeyguardCallback {
- oneway void onSurfaceControlCreated(in SurfaceControl remoteSurfaceControl);
+ oneway void onRemoteContentReady(in SurfaceControlViewHost.SurfacePackage surfacePackage);
oneway void onDismiss();
}
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index 6743a3f86944..9735f8157a57 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -74,6 +74,11 @@ public class PackageInfoLite implements Parcelable {
public boolean multiArch;
/**
+ * The android:debuggable flag from the package manifest.
+ */
+ public boolean debuggable;
+
+ /**
* Specifies the recommended install location. Can be one of
* {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal storage,
* {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external media,
@@ -108,6 +113,7 @@ public class PackageInfoLite implements Parcelable {
dest.writeInt(recommendedInstallLocation);
dest.writeInt(installLocation);
dest.writeInt(multiArch ? 1 : 0);
+ dest.writeInt(debuggable ? 1 : 0);
if (verifiers == null || verifiers.length == 0) {
dest.writeInt(0);
@@ -139,6 +145,7 @@ public class PackageInfoLite implements Parcelable {
recommendedInstallLocation = source.readInt();
installLocation = source.readInt();
multiArch = (source.readInt() != 0);
+ debuggable = (source.readInt() != 0);
final int verifiersLength = source.readInt();
if (verifiersLength == 0) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5a0bcf04c87f..9f7f4821a11e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -60,6 +60,7 @@ import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.util.AndroidException;
@@ -2997,6 +2998,18 @@ public abstract class PackageManager {
public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * the requisite kernel support to support incremental delivery aka Incremental FileSystem.
+ *
+ * @see IncrementalManager#isEnabled
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_INCREMENTAL_DELIVERY =
+ "android.software.incremental_delivery";
+
+ /**
* Extra field name for the URI to a verification file. Passed to a package
* verifier.
*
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 73b8a48d9153..7ebeb212b64a 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -216,6 +216,10 @@ public final class RollbackManager {
* across device reboot, by simulating what happens on reboot without
* actually rebooting the device.
*
+ * Note rollbacks in the process of enabling will be lost after calling
+ * this method since they are not persisted yet. Don't call this method
+ * in the middle of the install process.
+ *
* @throws SecurityException if the caller does not have appropriate permissions.
*
* @hide
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index a30fd6b51e76..dbf33cade60c 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -17,7 +17,6 @@
package android.hardware.soundtrigger;
import android.annotation.Nullable;
-import android.hardware.soundtrigger.ModelParams;
import android.media.AudioFormat;
import android.media.audio.common.AudioConfig;
import android.media.soundtrigger_middleware.AudioCapabilities;
@@ -333,20 +332,22 @@ class ConversionUtil {
public static int aidl2apiAudioCapabilities(int aidlCapabilities) {
int result = 0;
if ((aidlCapabilities & AudioCapabilities.ECHO_CANCELLATION) != 0) {
- result |= SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION;
+ result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION;
}
if ((aidlCapabilities & AudioCapabilities.NOISE_SUPPRESSION) != 0) {
- result |= SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION;
+ result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
}
return result;
}
public static int api2aidlAudioCapabilities(int apiCapabilities) {
int result = 0;
- if ((apiCapabilities & SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION) != 0) {
+ if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION)
+ != 0) {
result |= AudioCapabilities.ECHO_CANCELLATION;
}
- if ((apiCapabilities & SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION) != 0) {
+ if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION)
+ != 0) {
result |= AudioCapabilities.NOISE_SUPPRESSION;
}
return result;
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index fc4c02243b9c..a74871d29041 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -97,8 +97,8 @@ public class SoundTrigger {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "AUDIO_CAPABILITY_" }, value = {
- CAPABILITY_ECHO_CANCELLATION,
- CAPABILITY_NOISE_SUPPRESSION
+ AUDIO_CAPABILITY_ECHO_CANCELLATION,
+ AUDIO_CAPABILITY_NOISE_SUPPRESSION
})
public @interface AudioCapabilities {}
@@ -106,12 +106,12 @@ public class SoundTrigger {
* If set the underlying module supports AEC.
* Describes bit field {@link ModuleProperties#audioCapabilities}
*/
- public static final int CAPABILITY_ECHO_CANCELLATION = 0x1;
+ public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 0x1;
/**
* If set, the underlying module supports noise suppression.
* Describes bit field {@link ModuleProperties#audioCapabilities}
*/
- public static final int CAPABILITY_NOISE_SUPPRESSION = 0x2;
+ public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 0x2;
/** Unique module ID provided by the native service */
public final int id;
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index b128ea7f3e39..3c39d1558e87 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -202,7 +202,7 @@ public class ConnectivityDiagnosticsManager {
*/
@NetworkProbe
public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK =
- "networkProbesAttemped";
+ "networkProbesAttempted";
/** @hide */
@StringDef(prefix = {"KEY_"}, value = {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f644f148a5ad..a3059486a35a 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2423,14 +2423,14 @@ public class ConnectivityManager {
/**
* Get the set of tethered dhcp ranges.
*
- * @return an array of 0 or more {@code String} of tethered dhcp ranges.
- * @deprecated This API just return the default value which is not used in DhcpServer.
+ * @deprecated This method is not supported.
+ * TODO: remove this function when all of clients are removed.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
@Deprecated
public String[] getTetheredDhcpRanges() {
- return getTetheringManager().getTetheredDhcpRanges();
+ throw new UnsupportedOperationException("getTetheredDhcpRanges is not supported");
}
/**
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 25584f156084..3508b7031dd3 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -101,6 +101,8 @@ public final class Trace {
public static final long TRACE_TAG_NNAPI = 1L << 25;
/** @hide */
public static final long TRACE_TAG_RRO = 1L << 26;
+ /** @hide */
+ public static final long TRACE_TAG_APEX_MANAGER = 1L << 18;
private static final long TRACE_TAG_NOT_READY = 1L << 63;
private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 6ed1d2c345f8..b202053b5d65 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2325,10 +2325,12 @@ public class UserManager {
* @param restrictionKey the string key representing the restriction
* @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
*/
+ @TestApi
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.MANAGE_USERS)
- public boolean hasBaseUserRestriction(@UserRestrictionKey String restrictionKey,
- UserHandle userHandle) {
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
+ public boolean hasBaseUserRestriction(@UserRestrictionKey @NonNull String restrictionKey,
+ @NonNull UserHandle userHandle) {
try {
return mService.hasBaseUserRestriction(restrictionKey, userHandle.getIdentifier());
} catch (RemoteException re) {
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 35fa37a491de..0024ac7a6b2e 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -261,6 +261,13 @@ public final class IncrementalManager {
}
/**
+ * Checks if Incremental is enabled
+ */
+ public static boolean isEnabled() {
+ return nativeIsEnabled();
+ }
+
+ /**
* Checks if path is mounted on Incremental File System.
*/
public static boolean isIncrementalPath(@NonNull String path) {
@@ -268,5 +275,6 @@ public final class IncrementalManager {
}
/* Native methods */
+ private static native boolean nativeIsEnabled();
private static native boolean nativeIsIncrementalPath(@NonNull String path);
}
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 707426a22889..0edd01330fdd 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -345,9 +345,11 @@ public abstract class ContentCaptureService extends Service {
}
/**
- * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
+ * Notifies the service of {@link SnapshotData snapshot data} associated with an activity.
*
- * @param sessionId the session's Id
+ * @param sessionId the session's Id. This may also be
+ * {@link ContentCaptureSession#NO_SESSION_ID} if no content capture session
+ * exists for the activity being snapshotted
* @param snapshotData the data
*/
public void onActivitySnapshot(@NonNull ContentCaptureSessionId sessionId,
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index d0675ed4b99a..b4b5819c7d5f 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -126,22 +126,22 @@ public class TileService extends Service {
= "android.service.quicksettings.ACTIVE_TILE";
/**
- * Meta-data for a tile to support {@code BooleanState}.
+ * Meta-data for a tile to mark is toggleable.
* <p>
- * BooleanState is for tiles that should support switch tile behavior in accessibility. This is
+ * Toggleable tiles support switch tile behavior in accessibility. This is
* the behavior of most of the framework tiles.
*
- * To make a TileService support BooleanState, set this meta-data to true on the TileService's
- * manifest declaration.
+ * To indicate that a TileService is toggleable, set this meta-data to true on the
+ * TileService's manifest declaration.
* <pre class="prettyprint">
* {@literal
- * <meta-data android:name="android.service.quicksettings.BOOLEAN_TILE"
+ * <meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE"
* android:value="true" />
* }
* </pre>
*/
- public static final String META_DATA_BOOLEAN_TILE =
- "android.service.quicksettings.BOOLEAN_TILE";
+ public static final String META_DATA_TOGGLEABLE_TILE =
+ "android.service.quicksettings.TOGGLEABLE_TILE";
/**
* Used to notify SysUI that Listening has be requested.
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index b9b33d0214e3..a88d389178bf 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -181,14 +181,14 @@ public class AlwaysOnHotwordDetector {
* Returned by {@link #getSupportedAudioCapabilities()}
*/
public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION =
- SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION;
+ SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION;
/**
* If set, the underlying module supports noise suppression.
* Returned by {@link #getSupportedAudioCapabilities()}
*/
public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION =
- SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION;
+ SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 64635098f7f4..1d3e6d035bfe 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -66,46 +66,48 @@ public class ApkSignatureVerifier {
public static PackageParser.SigningDetails verify(String apkPath,
@SignatureSchemeVersion int minSignatureSchemeVersion)
throws PackageParserException {
+ return verifySignatures(apkPath, minSignatureSchemeVersion, true);
+ }
+
+ /**
+ * Returns the certificates associated with each signer for the given APK without verification.
+ * This method is dangerous and should not be used, unless the caller is absolutely certain the
+ * APK is trusted.
+ *
+ * @throws PackageParserException if there was a problem collecting certificates.
+ */
+ public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification(
+ String apkPath, int minSignatureSchemeVersion)
+ throws PackageParserException {
+ return verifySignatures(apkPath, minSignatureSchemeVersion, false);
+ }
+
+ /**
+ * Verifies the provided APK using all allowed signing schemas.
+ * @return the certificates associated with each signer.
+ * @param verifyFull whether to verify all contents of this APK or just collect certificates.
+ * @throws PackageParserException if there was a problem collecting certificates
+ */
+ private static PackageParser.SigningDetails verifySignatures(String apkPath,
+ @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
+ throws PackageParserException {
if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
// V3 and before are older than the requested minimum signing version
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
- + " or newer for package " + apkPath);
+ + " or newer for package " + apkPath);
}
// first try v3
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV3");
try {
- ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
- ApkSignatureSchemeV3Verifier.verify(apkPath);
- Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
- Signature[] signerSigs = convertToSignatures(signerCerts);
- Signature[] pastSignerSigs = null;
- if (vSigner.por != null) {
- // populate proof-of-rotation information
- pastSignerSigs = new Signature[vSigner.por.certs.size()];
- for (int i = 0; i < pastSignerSigs.length; i++) {
- pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
- pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
- }
- }
- return new PackageParser.SigningDetails(
- signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
- pastSignerSigs);
+ return verifyV3Signature(apkPath, verifyFull);
} catch (SignatureNotFoundException e) {
// not signed with v3, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v3 signature in package " + apkPath, e);
}
- } catch (Exception e) {
- // APK Signature Scheme v2 signature found but did not verify
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Failed to collect certificates from " + apkPath
- + " using APK Signature Scheme v3", e);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// redundant, protective version check
@@ -117,26 +119,14 @@ public class ApkSignatureVerifier {
}
// try v2
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
try {
- Certificate[][] signerCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
- Signature[] signerSigs = convertToSignatures(signerCerts);
-
- return new PackageParser.SigningDetails(
- signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V2);
+ return verifyV2Signature(apkPath, verifyFull);
} catch (SignatureNotFoundException e) {
// not signed with v2, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v2 signature in package " + apkPath, e);
}
- } catch (Exception e) {
- // APK Signature Scheme v2 signature found but did not verify
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Failed to collect certificates from " + apkPath
- + " using APK Signature Scheme v2", e);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// redundant, protective version check
@@ -148,14 +138,83 @@ public class ApkSignatureVerifier {
}
// v2 didn't work, try jarsigner
- return verifyV1Signature(apkPath, true);
+ return verifyV1Signature(apkPath, verifyFull);
}
/**
- * Verifies the provided APK and returns the certificates associated with each signer.
+ * Verifies the provided APK using V3 schema.
*
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
+ * @return the certificates associated with each signer.
+ * @throws SignatureNotFoundException if there are no V3 signatures in the APK
+ * @throws PackageParserException if there was a problem collecting certificates
+ */
+ private static PackageParser.SigningDetails verifyV3Signature(String apkPath,
+ boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV3" : "certsOnlyV3");
+ try {
+ ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
+ verifyFull ? ApkSignatureSchemeV3Verifier.verify(apkPath)
+ : ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(
+ apkPath);
+ Certificate[][] signerCerts = new Certificate[][]{vSigner.certs};
+ Signature[] signerSigs = convertToSignatures(signerCerts);
+ Signature[] pastSignerSigs = null;
+ if (vSigner.por != null) {
+ // populate proof-of-rotation information
+ pastSignerSigs = new Signature[vSigner.por.certs.size()];
+ for (int i = 0; i < pastSignerSigs.length; i++) {
+ pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
+ pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
+ }
+ }
+ return new PackageParser.SigningDetails(signerSigs,
+ SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs);
+ } catch (SignatureNotFoundException e) {
+ throw e;
+ } catch (Exception e) {
+ // APK Signature Scheme v3 signature found but did not verify
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "Failed to collect certificates from " + apkPath
+ + " using APK Signature Scheme v3", e);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ /**
+ * Verifies the provided APK using V2 schema.
*
+ * @param verifyFull whether to verify all contents of this APK or just collect certificates.
+ * @return the certificates associated with each signer.
+ * @throws SignatureNotFoundException if there are no V2 signatures in the APK
+ * @throws PackageParserException if there was a problem collecting certificates
+ */
+ private static PackageParser.SigningDetails verifyV2Signature(String apkPath,
+ boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV2" : "certsOnlyV2");
+ try {
+ Certificate[][] signerCerts = verifyFull ? ApkSignatureSchemeV2Verifier.verify(apkPath)
+ : ApkSignatureSchemeV2Verifier.unsafeGetCertsWithoutVerification(apkPath);
+ Signature[] signerSigs = convertToSignatures(signerCerts);
+ return new PackageParser.SigningDetails(signerSigs,
+ SignatureSchemeVersion.SIGNING_BLOCK_V2);
+ } catch (SignatureNotFoundException e) {
+ throw e;
+ } catch (Exception e) {
+ // APK Signature Scheme v2 signature found but did not verify
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "Failed to collect certificates from " + apkPath
+ + " using APK Signature Scheme v2", e);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ /**
+ * Verifies the provided APK using JAR schema.
+ * @return the certificates associated with each signer.
+ * @param verifyFull whether to verify all contents of this APK or just collect certificates.
* @throws PackageParserException if there was a problem collecting certificates
*/
private static PackageParser.SigningDetails verifyV1Signature(
@@ -277,7 +336,7 @@ public class ApkSignatureVerifier {
*
* @throws CertificateEncodingException if it is unable to create a Signature object.
*/
- public static Signature[] convertToSignatures(Certificate[][] certs)
+ private static Signature[] convertToSignatures(Certificate[][] certs)
throws CertificateEncodingException {
final Signature[] res = new Signature[certs.length];
for (int i = 0; i < certs.length; i++) {
@@ -296,99 +355,29 @@ public class ApkSignatureVerifier {
}
/**
- * Returns the certificates associated with each signer for the given APK without verification.
- * This method is dangerous and should not be used, unless the caller is absolutely certain the
- * APK is trusted.
- *
- * @throws PackageParserException if the APK's signature failed to verify.
- * or greater is not found, except in the case of no JAR signature.
+ * Returns the minimum signature scheme version required for an app targeting the specified
+ * {@code targetSdk}.
*/
- public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification(
- String apkPath, int minSignatureSchemeVersion)
- throws PackageParserException {
-
- if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
- // V3 and before are older than the requested minimum signing version
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No signature found in package of version " + minSignatureSchemeVersion
- + " or newer for package " + apkPath);
- }
-
- // first try v3
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV3");
- try {
- ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
- ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
- Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
- Signature[] signerSigs = convertToSignatures(signerCerts);
- Signature[] pastSignerSigs = null;
- if (vSigner.por != null) {
- // populate proof-of-rotation information
- pastSignerSigs = new Signature[vSigner.por.certs.size()];
- for (int i = 0; i < pastSignerSigs.length; i++) {
- pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
- pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
- }
- }
- return new PackageParser.SigningDetails(
- signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
- pastSignerSigs);
- } catch (SignatureNotFoundException e) {
- // not signed with v3, try older if allowed
- if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No APK Signature Scheme v3 signature in package " + apkPath, e);
- }
- } catch (Exception e) {
- // APK Signature Scheme v3 signature found but did not verify
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Failed to collect certificates from " + apkPath
- + " using APK Signature Scheme v3", e);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- // redundant, protective version check
- if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
- // V2 and before are older than the requested minimum signing version
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No signature found in package of version " + minSignatureSchemeVersion
- + " or newer for package " + apkPath);
+ public static int getMinimumSignatureSchemeVersionForTargetSdk(int targetSdk) {
+ if (targetSdk >= Build.VERSION_CODES.R) {
+ return SignatureSchemeVersion.SIGNING_BLOCK_V2;
}
+ return SignatureSchemeVersion.JAR;
+ }
- // first try v2
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV2");
- try {
- Certificate[][] signerCerts =
- ApkSignatureSchemeV2Verifier.unsafeGetCertsWithoutVerification(apkPath);
- Signature[] signerSigs = convertToSignatures(signerCerts);
- return new PackageParser.SigningDetails(signerSigs,
- SignatureSchemeVersion.SIGNING_BLOCK_V2);
- } catch (SignatureNotFoundException e) {
- // not signed with v2, try older if allowed
- if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No APK Signature Scheme v2 signature in package " + apkPath, e);
- }
- } catch (Exception e) {
- // APK Signature Scheme v2 signature found but did not verify
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Failed to collect certificates from " + apkPath
- + " using APK Signature Scheme v2", e);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
+ /**
+ * Result of a successful APK verification operation.
+ */
+ public static class Result {
+ public final Certificate[][] certs;
+ public final Signature[] sigs;
+ public final int signatureSchemeVersion;
- // redundant, protective version check
- if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
- // V1 and is older than the requested minimum signing version
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No signature found in package of version " + minSignatureSchemeVersion
- + " or newer for package " + apkPath);
+ public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
+ this.certs = certs;
+ this.sigs = sigs;
+ this.signatureSchemeVersion = signingVersion;
}
-
- // v2 didn't work, try jarsigner
- return verifyV1Signature(apkPath, false);
}
/**
@@ -416,7 +405,7 @@ public class ApkSignatureVerifier {
*/
public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
throws IOException, SignatureNotFoundException, SecurityException, DigestException,
- NoSuchAlgorithmException {
+ NoSuchAlgorithmException {
// first try v3
try {
return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory);
@@ -446,30 +435,4 @@ public class ApkSignatureVerifier {
return null;
}
}
-
- /**
- * Returns the minimum signature scheme version required for an app targeting the specified
- * {@code targetSdk}.
- */
- public static int getMinimumSignatureSchemeVersionForTargetSdk(int targetSdk) {
- if (targetSdk >= Build.VERSION_CODES.R) {
- return SignatureSchemeVersion.SIGNING_BLOCK_V2;
- }
- return SignatureSchemeVersion.JAR;
- }
-
- /**
- * Result of a successful APK verification operation.
- */
- public static class Result {
- public final Certificate[][] certs;
- public final Signature[] sigs;
- public final int signatureSchemeVersion;
-
- public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
- this.certs = certs;
- this.sigs = sigs;
- this.signatureSchemeVersion = signingVersion;
- }
- }
}
diff --git a/core/java/android/view/SurfaceControlViewHost.aidl b/core/java/android/view/SurfaceControlViewHost.aidl
new file mode 100644
index 000000000000..3b31ab834a51
--- /dev/null
+++ b/core/java/android/view/SurfaceControlViewHost.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.view;
+
+parcelable SurfaceControlViewHost.SurfacePackage; \ No newline at end of file
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 159b93eb12dd..2c30d29d8da2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -222,26 +222,26 @@ public final class ViewRootImpl implements ViewParent,
* @see #USE_NEW_INSETS_PROPERTY
* @hide
*/
- public static int sNewInsetsMode =
- SystemProperties.getInt(USE_NEW_INSETS_PROPERTY, 0);
+ public static final int NEW_INSETS_MODE_NONE = 0;
/**
* @see #USE_NEW_INSETS_PROPERTY
* @hide
*/
- public static final int NEW_INSETS_MODE_NONE = 0;
+ public static final int NEW_INSETS_MODE_IME = 1;
/**
* @see #USE_NEW_INSETS_PROPERTY
* @hide
*/
- public static final int NEW_INSETS_MODE_IME = 1;
+ public static final int NEW_INSETS_MODE_FULL = 2;
/**
* @see #USE_NEW_INSETS_PROPERTY
* @hide
*/
- public static final int NEW_INSETS_MODE_FULL = 2;
+ public static int sNewInsetsMode =
+ SystemProperties.getInt(USE_NEW_INSETS_PROPERTY, NEW_INSETS_MODE_IME);
/**
* Set this system property to true to force the view hierarchy to render
diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java
index 33f21f211a1a..cf34b0bad78d 100644
--- a/core/java/android/view/WindowContainerTransaction.java
+++ b/core/java/android/view/WindowContainerTransaction.java
@@ -16,6 +16,8 @@
package android.view;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -25,6 +27,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
/**
@@ -36,10 +40,14 @@ import java.util.Map;
public class WindowContainerTransaction implements Parcelable {
private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>();
+ // Flat list because re-order operations are order-dependent
+ private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();
+
public WindowContainerTransaction() {}
protected WindowContainerTransaction(Parcel in) {
in.readMap(mChanges, null /* loader */);
+ in.readList(mHierarchyOps, null /* loader */);
}
private Change getOrCreateChange(IBinder token) {
@@ -97,10 +105,39 @@ public class WindowContainerTransaction implements Parcelable {
return this;
}
+ /**
+ * Reparents a container into another one. The effect of a {@code null} parent can vary. For
+ * example, reparenting a stack to {@code null} will reparent it to its display.
+ *
+ * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
+ * the bottom.
+ */
+ public WindowContainerTransaction reparent(@NonNull IWindowContainer child,
+ @Nullable IWindowContainer parent, boolean onTop) {
+ mHierarchyOps.add(new HierarchyOp(child.asBinder(),
+ parent == null ? null : parent.asBinder(), onTop));
+ return this;
+ }
+
+ /**
+ * Reorders a container within its parent.
+ *
+ * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
+ * the bottom.
+ */
+ public WindowContainerTransaction reorder(@NonNull IWindowContainer child, boolean onTop) {
+ mHierarchyOps.add(new HierarchyOp(child.asBinder(), onTop));
+ return this;
+ }
+
public Map<IBinder, Change> getChanges() {
return mChanges;
}
+ public List<HierarchyOp> getHierarchyOps() {
+ return mHierarchyOps;
+ }
+
@Override
public String toString() {
return "WindowContainerTransaction { changes = " + mChanges + " }";
@@ -109,6 +146,7 @@ public class WindowContainerTransaction implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeMap(mChanges);
+ dest.writeList(mHierarchyOps);
}
@Override
@@ -249,4 +287,88 @@ public class WindowContainerTransaction implements Parcelable {
}
};
}
+
+ /**
+ * Holds information about a reparent/reorder operation in the hierarchy. This is separate from
+ * Changes because they must be executed in the same order that they are added.
+ */
+ public static class HierarchyOp implements Parcelable {
+ private final IBinder mContainer;
+
+ // If this is same as mContainer, then only change position, don't reparent.
+ private final IBinder mReparent;
+
+ // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
+ private final boolean mToTop;
+
+ public HierarchyOp(@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
+ mContainer = container;
+ mReparent = reparent;
+ mToTop = toTop;
+ }
+
+ public HierarchyOp(@NonNull IBinder container, boolean toTop) {
+ mContainer = container;
+ mReparent = container;
+ mToTop = toTop;
+ }
+
+ protected HierarchyOp(Parcel in) {
+ mContainer = in.readStrongBinder();
+ mReparent = in.readStrongBinder();
+ mToTop = in.readBoolean();
+ }
+
+ public boolean isReparent() {
+ return mContainer != mReparent;
+ }
+
+ @Nullable
+ public IBinder getNewParent() {
+ return mReparent;
+ }
+
+ @NonNull
+ public IBinder getContainer() {
+ return mContainer;
+ }
+
+ public boolean getToTop() {
+ return mToTop;
+ }
+
+ @Override
+ public String toString() {
+ if (isReparent()) {
+ return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
+ + mReparent + "}";
+ } else {
+ return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mContainer);
+ dest.writeStrongBinder(mReparent);
+ dest.writeBoolean(mToTop);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<HierarchyOp> CREATOR = new Creator<HierarchyOp>() {
+ @Override
+ public HierarchyOp createFromParcel(Parcel in) {
+ return new HierarchyOp(in);
+ }
+
+ @Override
+ public HierarchyOp[] newArray(int size) {
+ return new HierarchyOp[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 05cf3edb4525..bf2de14e6811 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -4854,8 +4854,11 @@ public class AccessibilityNodeInfo implements Parcelable {
new AccessibilityAction(R.id.accessibilityActionPressAndHold);
/**
- * Action to send ime action. A node should expose this action only for views that are
- * currently with input focus and editable.
+ * Action to send an ime action which is from
+ * {@link android.view.inputmethod.EditorInfo#actionId}. This action would be
+ * {@link android.view.inputmethod.EditorInfo#IME_ACTION_UNSPECIFIED} if no specific
+ * actionId defined. A node should expose this action only for views that are currently
+ * with input focus and editable.
*/
@NonNull public static final AccessibilityAction ACTION_IME_ENTER =
new AccessibilityAction(R.id.accessibilityActionImeEnter);
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 232d96ba7f43..2134dab7986b 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -22,6 +22,7 @@ import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.util.DebugUtils;
import android.util.Log;
import android.view.View;
@@ -50,7 +51,11 @@ public abstract class ContentCaptureSession implements AutoCloseable {
private static final Random sIdGenerator = new Random();
- /** @hide */
+ /**
+ * ID used to indicate that a session does not exist
+ * @hide
+ */
+ @SystemApi
public static final int NO_SESSION_ID = 0;
/**
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 703b64f8224f..024de4de92e7 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -80,7 +80,6 @@ public final class InlineSuggestionInfo implements Parcelable {
@NonNull InlinePresentationSpec presentationSpec,
@NonNull @Source String source,
@Nullable String[] autofillHints) {
- // TODO(b/147394280): Add CTS test for the type field.
return new InlineSuggestionInfo(presentationSpec, source, autofillHints, TYPE_SUGGESTION);
}
diff --git a/core/java/android/webkit/WebResourceRequest.java b/core/java/android/webkit/WebResourceRequest.java
index 964b6f8e259d..0b307e63ca45 100644
--- a/core/java/android/webkit/WebResourceRequest.java
+++ b/core/java/android/webkit/WebResourceRequest.java
@@ -32,10 +32,10 @@ public interface WebResourceRequest {
Uri getUrl();
/**
- * Gets whether the request was made for the main frame.
+ * Gets whether the request was made in order to fetch the main frame's document.
*
- * @return whether the request was made for the main frame. Will be {@code false} for iframes,
- * for example.
+ * @return whether the request was made for the main frame document. Will be
+ * {@code false} for subresources or iframes, for example.
*/
boolean isForMainFrame();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cbfa05caac6e..469ab2ee8f4a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -422,9 +422,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
static final int PROCESS_TEXT_REQUEST_CODE = 100;
- // Accessibility action to send IME custom action for CTS testing.
- public static final int ACCESSIBILITY_ACTION_IME_ENTER = R.id.accessibilityActionImeEnter;
-
/**
* Return code of {@link #doKeyDown}.
*/
@@ -11758,7 +11755,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
AccessibilityNodeInfo.AccessibilityAction action =
new AccessibilityNodeInfo.AccessibilityAction(
- ACCESSIBILITY_ACTION_IME_ENTER, imeActionLabel);
+ R.id.accessibilityActionImeEnter, imeActionLabel);
info.addAction(action);
}
}
@@ -12072,7 +12069,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
} return true;
- case ACCESSIBILITY_ACTION_IME_ENTER: {
+ case R.id.accessibilityActionImeEnter: {
if (isFocused() && isTextEditable()) {
final int imeActionId = (arguments != null) ? arguments.getInt(
AccessibilityNodeInfo.ACTION_ARGUMENT_IME_ACTION_ID_INT,
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 82eb55a30a4e..5f35622cad51 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -471,6 +471,7 @@ public class AccessibilityButtonChooserActivity extends Activity {
holder.mSwitchItem.setVisibility(View.GONE);
holder.mItemContainer.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE);
holder.mItemView.setEnabled(enabledState);
+ holder.mItemView.setClickable(!enabledState);
}
private void updateInvisibleActionItemVisibility(@NonNull Context context,
@@ -485,6 +486,7 @@ public class AccessibilityButtonChooserActivity extends Activity {
holder.mItemContainer.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
? View.VISIBLE : View.GONE);
holder.mItemView.setEnabled(true);
+ holder.mItemView.setClickable(false);
}
private void updateIntuitiveActionItemVisibility(@NonNull Context context,
@@ -504,6 +506,7 @@ public class AccessibilityButtonChooserActivity extends Activity {
holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled);
holder.mItemContainer.setVisibility(View.VISIBLE);
holder.mItemView.setEnabled(true);
+ holder.mItemView.setClickable(false);
}
private void updateBounceActionItemVisibility(@NonNull Context context,
@@ -518,6 +521,7 @@ public class AccessibilityButtonChooserActivity extends Activity {
holder.mSwitchItem.setVisibility(View.GONE);
holder.mItemContainer.setVisibility(View.VISIBLE);
holder.mItemView.setEnabled(true);
+ holder.mItemView.setClickable(false);
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index bbb751359bab..a934de328989 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1487,7 +1487,6 @@ public class ResolverActivity extends Activity implements
for (int i = 0; i < tabWidget.getChildCount(); i++) {
TextView title = tabWidget.getChildAt(i).findViewById(android.R.id.title);
title.setTextColor(getColor(R.color.resolver_tabs_inactive_color));
- title.setAllCaps(false);
}
}
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 22fe31eeda38..a0de51d4d211 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -73,6 +73,7 @@ public class NativeLibraryHelper {
private final CloseGuard mGuard = CloseGuard.get();
private volatile boolean mClosed;
+ final String[] apkPaths;
final long[] apkHandles;
final boolean multiArch;
final boolean extractNativeLibs;
@@ -103,9 +104,11 @@ public class NativeLibraryHelper {
private static Handle create(List<String> codePaths, boolean multiArch,
boolean extractNativeLibs, boolean debuggable) throws IOException {
final int size = codePaths.size();
+ final String[] apkPaths = new String[size];
final long[] apkHandles = new long[size];
for (int i = 0; i < size; i++) {
final String path = codePaths.get(i);
+ apkPaths[i] = path;
apkHandles[i] = nativeOpenApk(path);
if (apkHandles[i] == 0) {
// Unwind everything we've opened so far
@@ -116,7 +119,7 @@ public class NativeLibraryHelper {
}
}
- return new Handle(apkHandles, multiArch, extractNativeLibs, debuggable);
+ return new Handle(apkPaths, apkHandles, multiArch, extractNativeLibs, debuggable);
}
public static Handle createFd(PackageLite lite, FileDescriptor fd) throws IOException {
@@ -127,11 +130,13 @@ public class NativeLibraryHelper {
throw new IOException("Unable to open APK " + path + " from fd " + fd);
}
- return new Handle(apkHandles, lite.multiArch, lite.extractNativeLibs, lite.debuggable);
+ return new Handle(new String[]{path}, apkHandles, lite.multiArch,
+ lite.extractNativeLibs, lite.debuggable);
}
- Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs,
- boolean debuggable) {
+ Handle(String[] apkPaths, long[] apkHandles, boolean multiArch,
+ boolean extractNativeLibs, boolean debuggable) {
+ this.apkPaths = apkPaths;
this.apkHandles = apkHandles;
this.multiArch = multiArch;
this.extractNativeLibs = extractNativeLibs;
@@ -313,40 +318,58 @@ public class NativeLibraryHelper {
}
public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
- String[] abiList, boolean useIsaSubdir) throws IOException {
- createNativeLibrarySubdir(libraryRoot);
-
+ String[] abiList, boolean useIsaSubdir, boolean isIncremental) throws IOException {
/*
* If this is an internal application or our nativeLibraryPath points to
* the app-lib directory, unpack the libraries if necessary.
*/
int abi = findSupportedAbi(handle, abiList);
- if (abi >= 0) {
- /*
- * If we have a matching instruction set, construct a subdir under the native
- * library root that corresponds to this instruction set.
- */
- final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]);
- final File subDir;
- if (useIsaSubdir) {
- final File isaSubdir = new File(libraryRoot, instructionSet);
- createNativeLibrarySubdir(isaSubdir);
- subDir = isaSubdir;
- } else {
- subDir = libraryRoot;
- }
+ if (abi < 0) {
+ return abi;
+ }
- int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]);
- if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
- return copyRet;
+ /*
+ * If we have a matching instruction set, construct a subdir under the native
+ * library root that corresponds to this instruction set.
+ */
+ final String supportedAbi = abiList[abi];
+ final String instructionSet = VMRuntime.getInstructionSet(supportedAbi);
+ final File subDir;
+ if (useIsaSubdir) {
+ subDir = new File(libraryRoot, instructionSet);
+ } else {
+ subDir = libraryRoot;
+ }
+
+ if (isIncremental) {
+ int res =
+ incrementalConfigureNativeBinariesForSupportedAbi(handle, subDir, supportedAbi);
+ if (res != PackageManager.INSTALL_SUCCEEDED) {
+ // TODO(b/133435829): the caller of this function expects that we return the index
+ // to the supported ABI. However, any non-negative integer can be a valid index.
+ // We should fix this function and make sure it doesn't accidentally return an error
+ // code that can also be a valid index.
+ return res;
}
+ return abi;
+ }
+
+ // For non-incremental, use regular extraction and copy
+ createNativeLibrarySubdir(libraryRoot);
+ if (subDir != libraryRoot) {
+ createNativeLibrarySubdir(subDir);
+ }
+
+ int copyRet = copyNativeBinaries(handle, subDir, supportedAbi);
+ if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
+ return copyRet;
}
return abi;
}
public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
- String abiOverride) {
+ String abiOverride, boolean isIncremental) {
try {
if (handle.multiArch) {
// Warn if we've set an abiOverride for multi-lib packages..
@@ -359,7 +382,8 @@ public class NativeLibraryHelper {
int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
- Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
+ Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */,
+ isIncremental);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet);
@@ -369,7 +393,8 @@ public class NativeLibraryHelper {
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
- Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
+ Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */,
+ isIncremental);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet);
@@ -392,7 +417,7 @@ public class NativeLibraryHelper {
}
int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList,
- true /* use isa specific subdirs */);
+ true /* use isa specific subdirs */, isIncremental);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
return copyRet;
@@ -449,29 +474,15 @@ public class NativeLibraryHelper {
* Service will create native library directories and set up native library binary files in the
* same structure as they are in non-incremental installations.
*
- * @param pkg The package to be installed, including all the APK files.
- * @param handle The pointer to an zip archive.
- * @param libraryRoot The root directory of the native library files, e.g., lib/
- * @param abiList The list of ABIs that are supported by the current device.
- * @param useIsaSubdir Whether or not to set up a sub dir for the ISA.
- * @return ABI code if installation succeeds or error code if installation fails.
+ * @param handle The Handle object that contains all apk paths.
+ * @param libSubDir The target directory to put the native library files, e.g., lib/ or lib/arm
+ * @param abi The abi that is supported by the current device.
+ * @return Integer code if installation succeeds or fails.
*/
- public static int configureNativeBinariesForSupportedAbi(AndroidPackage pkg, Handle handle,
- File libraryRoot, String[] abiList, boolean useIsaSubdir) {
- int abi = findSupportedAbi(handle, abiList);
- if (abi < 0) {
- Slog.e(TAG, "Failed to find find matching ABI.");
- return abi;
- }
-
- // Currently only support installations that have pre-configured native library files
- // TODO(b/136132412): implement this after incfs supports file mapping
- if (!libraryRoot.exists()) {
- Slog.e(TAG, "Incremental installation currently does not configure native libs");
- return INSTALL_FAILED_NO_MATCHING_ABIS;
- }
-
- return abi;
+ private static int incrementalConfigureNativeBinariesForSupportedAbi(Handle handle,
+ File libSubDir, String abi) {
+ // TODO(b/136132412): implement this
+ return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
}
// We don't care about the other return values for now.
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 74b481c938c3..26d2f19b13b6 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -29,6 +29,7 @@ import android.os.FileUtils;
import android.os.Process;
import android.os.SystemProperties;
import android.os.Trace;
+import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.permission.PermissionManager.SplitPermissionInfo;
import android.text.TextUtils;
@@ -1156,6 +1157,10 @@ public class SystemConfig {
addFeature(PackageManager.FEATURE_RAM_NORMAL, 0);
}
+ if (IncrementalManager.isEnabled()) {
+ addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY, 0);
+ }
+
for (String featureName : mUnavailableFeatures) {
removeFeature(featureName);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 35eb0fc986d7..3d0926d61789 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -153,7 +153,7 @@ cc_library_shared {
"android_util_MemoryIntArray.cpp",
"android_util_Process.cpp",
"android_util_jar_StrictJarFile.cpp",
- "android_media_AudioDeviceAddress.cpp",
+ "android_media_AudioDevice.cpp",
"android_media_AudioEffectDescriptor.cpp",
"android_media_AudioRecord.cpp",
"android_media_AudioSystem.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 657336e6910a..d0e8fd355199 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -86,7 +86,7 @@ extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env);
extern int register_android_hardware_UsbRequest(JNIEnv *env);
extern int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env);
-extern int register_android_media_AudioDeviceAddress(JNIEnv *env);
+extern int register_android_media_AudioDevice(JNIEnv* env);
extern int register_android_media_AudioEffectDescriptor(JNIEnv *env);
extern int register_android_media_AudioRecord(JNIEnv *env);
extern int register_android_media_AudioSystem(JNIEnv *env);
@@ -631,6 +631,8 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
char foregroundHeapGrowthMultiplierOptsBuf[
sizeof("-XX:ForegroundHeapGrowthMultiplier=")-1 + PROPERTY_VALUE_MAX];
+ char finalizerTimeoutMsOptsBuf[sizeof("-XX:FinalizerTimeoutMs=")-1 + PROPERTY_VALUE_MAX];
+ char threadSuspendTimeoutOptsBuf[sizeof("-XX:ThreadSuspendTimeout=")-1 + PROPERTY_VALUE_MAX];
char cachePruneBuf[sizeof("-Xzygote-max-boot-retry=")-1 + PROPERTY_VALUE_MAX];
char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
char dex2oatXmxImageFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
@@ -784,7 +786,15 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
parseRuntimeOption("dalvik.vm.foreground-heap-growth-multiplier",
foregroundHeapGrowthMultiplierOptsBuf,
"-XX:ForegroundHeapGrowthMultiplier=");
-
+ /*
+ * Finalizer and thread suspend timeouts.
+ */
+ parseRuntimeOption("dalvik.vm.finalizer-timeout-ms",
+ finalizerTimeoutMsOptsBuf,
+ "-XX:FinalizerTimeoutMs=");
+ parseRuntimeOption("dalvik.vm.thread-suspend-timeout-ms",
+ threadSuspendTimeoutOptsBuf,
+ "-XX:ThreadSuspendTimeout=");
/*
* JIT related options.
*/
@@ -1424,140 +1434,140 @@ static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env
}
static const RegJNIRec gRegJNI[] = {
- REG_JNI(register_com_android_internal_os_RuntimeInit),
- REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
- REG_JNI(register_android_os_SystemClock),
- REG_JNI(register_android_util_EventLog),
- REG_JNI(register_android_util_Log),
- REG_JNI(register_android_util_MemoryIntArray),
- REG_JNI(register_android_util_StatsLog),
- REG_JNI(register_android_util_StatsLogInternal),
- REG_JNI(register_android_app_admin_SecurityLog),
- REG_JNI(register_android_content_AssetManager),
- REG_JNI(register_android_content_StringBlock),
- REG_JNI(register_android_content_XmlBlock),
- REG_JNI(register_android_content_res_ApkAssets),
- REG_JNI(register_android_text_AndroidCharacter),
- REG_JNI(register_android_text_Hyphenator),
- REG_JNI(register_android_view_InputDevice),
- REG_JNI(register_android_view_KeyCharacterMap),
- REG_JNI(register_android_os_Process),
- REG_JNI(register_android_os_SystemProperties),
- REG_JNI(register_android_os_Binder),
- REG_JNI(register_android_os_Parcel),
- REG_JNI(register_android_os_HidlMemory),
- REG_JNI(register_android_os_HidlSupport),
- REG_JNI(register_android_os_HwBinder),
- REG_JNI(register_android_os_HwBlob),
- REG_JNI(register_android_os_HwParcel),
- REG_JNI(register_android_os_HwRemoteBinder),
- REG_JNI(register_android_os_NativeHandle),
- REG_JNI(register_android_os_storage_StorageManager),
- REG_JNI(register_android_os_VintfObject),
- REG_JNI(register_android_os_VintfRuntimeInfo),
- REG_JNI(register_android_service_DataLoaderService),
- REG_JNI(register_android_view_DisplayEventReceiver),
- REG_JNI(register_android_view_RenderNodeAnimator),
- REG_JNI(register_android_view_InputApplicationHandle),
- REG_JNI(register_android_view_InputWindowHandle),
- REG_JNI(register_android_view_Surface),
- REG_JNI(register_android_view_SurfaceControl),
- REG_JNI(register_android_view_SurfaceSession),
- REG_JNI(register_android_view_CompositionSamplingListener),
- REG_JNI(register_android_view_TextureView),
- REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper),
- REG_JNI(register_com_google_android_gles_jni_EGLImpl),
- REG_JNI(register_com_google_android_gles_jni_GLImpl),
- REG_JNI(register_android_opengl_jni_EGL14),
- REG_JNI(register_android_opengl_jni_EGL15),
- REG_JNI(register_android_opengl_jni_EGLExt),
- REG_JNI(register_android_opengl_jni_GLES10),
- REG_JNI(register_android_opengl_jni_GLES10Ext),
- REG_JNI(register_android_opengl_jni_GLES11),
- REG_JNI(register_android_opengl_jni_GLES11Ext),
- REG_JNI(register_android_opengl_jni_GLES20),
- REG_JNI(register_android_opengl_jni_GLES30),
- REG_JNI(register_android_opengl_jni_GLES31),
- REG_JNI(register_android_opengl_jni_GLES31Ext),
- REG_JNI(register_android_opengl_jni_GLES32),
- REG_JNI(register_android_graphics_classes),
- REG_JNI(register_android_graphics_BLASTBufferQueue),
- REG_JNI(register_android_graphics_GraphicBuffer),
- REG_JNI(register_android_database_CursorWindow),
- REG_JNI(register_android_database_SQLiteConnection),
- REG_JNI(register_android_database_SQLiteGlobal),
- REG_JNI(register_android_database_SQLiteDebug),
- REG_JNI(register_android_os_Debug),
- REG_JNI(register_android_os_FileObserver),
- REG_JNI(register_android_os_GraphicsEnvironment),
- REG_JNI(register_android_os_MessageQueue),
- REG_JNI(register_android_os_SELinux),
- REG_JNI(register_android_os_Trace),
- REG_JNI(register_android_os_UEventObserver),
- REG_JNI(register_android_net_LocalSocketImpl),
- REG_JNI(register_android_net_NetworkUtils),
- REG_JNI(register_android_os_MemoryFile),
- REG_JNI(register_android_os_SharedMemory),
- REG_JNI(register_android_os_incremental_IncrementalManager),
- REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
- REG_JNI(register_com_android_internal_os_Zygote),
- REG_JNI(register_com_android_internal_os_ZygoteInit),
- REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
- REG_JNI(register_android_hardware_Camera),
- REG_JNI(register_android_hardware_camera2_CameraMetadata),
- REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
- REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),
- REG_JNI(register_android_hardware_camera2_DngCreator),
- REG_JNI(register_android_hardware_HardwareBuffer),
- REG_JNI(register_android_hardware_SensorManager),
- REG_JNI(register_android_hardware_SerialPort),
- REG_JNI(register_android_hardware_UsbDevice),
- REG_JNI(register_android_hardware_UsbDeviceConnection),
- REG_JNI(register_android_hardware_UsbRequest),
- REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
- REG_JNI(register_android_media_AudioDeviceAddress),
- REG_JNI(register_android_media_AudioEffectDescriptor),
- REG_JNI(register_android_media_AudioSystem),
- REG_JNI(register_android_media_AudioRecord),
- REG_JNI(register_android_media_AudioTrack),
- REG_JNI(register_android_media_AudioAttributes),
- REG_JNI(register_android_media_AudioProductStrategies),
- REG_JNI(register_android_media_AudioVolumeGroups),
- REG_JNI(register_android_media_AudioVolumeGroupChangeHandler),
- REG_JNI(register_android_media_MediaMetrics),
- REG_JNI(register_android_media_MicrophoneInfo),
- REG_JNI(register_android_media_RemoteDisplay),
- REG_JNI(register_android_media_ToneGenerator),
- REG_JNI(register_android_media_midi),
-
- REG_JNI(register_android_opengl_classes),
- REG_JNI(register_android_server_NetworkManagementSocketTagger),
- REG_JNI(register_android_ddm_DdmHandleNativeHeap),
- REG_JNI(register_android_backup_BackupDataInput),
- REG_JNI(register_android_backup_BackupDataOutput),
- REG_JNI(register_android_backup_FileBackupHelperBase),
- REG_JNI(register_android_backup_BackupHelperDispatcher),
- REG_JNI(register_android_app_backup_FullBackup),
- REG_JNI(register_android_app_Activity),
- REG_JNI(register_android_app_ActivityThread),
- REG_JNI(register_android_app_NativeActivity),
- REG_JNI(register_android_util_jar_StrictJarFile),
- REG_JNI(register_android_view_InputChannel),
- REG_JNI(register_android_view_InputEventReceiver),
- REG_JNI(register_android_view_InputEventSender),
- REG_JNI(register_android_view_InputQueue),
- REG_JNI(register_android_view_KeyEvent),
- REG_JNI(register_android_view_MotionEvent),
- REG_JNI(register_android_view_PointerIcon),
- REG_JNI(register_android_view_VelocityTracker),
-
- REG_JNI(register_android_content_res_ObbScanner),
- REG_JNI(register_android_content_res_Configuration),
-
- REG_JNI(register_android_animation_PropertyValuesHolder),
- REG_JNI(register_android_security_Scrypt),
- REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
- REG_JNI(register_com_android_internal_os_FuseAppLoop),
+ REG_JNI(register_com_android_internal_os_RuntimeInit),
+ REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
+ REG_JNI(register_android_os_SystemClock),
+ REG_JNI(register_android_util_EventLog),
+ REG_JNI(register_android_util_Log),
+ REG_JNI(register_android_util_MemoryIntArray),
+ REG_JNI(register_android_util_StatsLog),
+ REG_JNI(register_android_util_StatsLogInternal),
+ REG_JNI(register_android_app_admin_SecurityLog),
+ REG_JNI(register_android_content_AssetManager),
+ REG_JNI(register_android_content_StringBlock),
+ REG_JNI(register_android_content_XmlBlock),
+ REG_JNI(register_android_content_res_ApkAssets),
+ REG_JNI(register_android_text_AndroidCharacter),
+ REG_JNI(register_android_text_Hyphenator),
+ REG_JNI(register_android_view_InputDevice),
+ REG_JNI(register_android_view_KeyCharacterMap),
+ REG_JNI(register_android_os_Process),
+ REG_JNI(register_android_os_SystemProperties),
+ REG_JNI(register_android_os_Binder),
+ REG_JNI(register_android_os_Parcel),
+ REG_JNI(register_android_os_HidlMemory),
+ REG_JNI(register_android_os_HidlSupport),
+ REG_JNI(register_android_os_HwBinder),
+ REG_JNI(register_android_os_HwBlob),
+ REG_JNI(register_android_os_HwParcel),
+ REG_JNI(register_android_os_HwRemoteBinder),
+ REG_JNI(register_android_os_NativeHandle),
+ REG_JNI(register_android_os_storage_StorageManager),
+ REG_JNI(register_android_os_VintfObject),
+ REG_JNI(register_android_os_VintfRuntimeInfo),
+ REG_JNI(register_android_service_DataLoaderService),
+ REG_JNI(register_android_view_DisplayEventReceiver),
+ REG_JNI(register_android_view_RenderNodeAnimator),
+ REG_JNI(register_android_view_InputApplicationHandle),
+ REG_JNI(register_android_view_InputWindowHandle),
+ REG_JNI(register_android_view_Surface),
+ REG_JNI(register_android_view_SurfaceControl),
+ REG_JNI(register_android_view_SurfaceSession),
+ REG_JNI(register_android_view_CompositionSamplingListener),
+ REG_JNI(register_android_view_TextureView),
+ REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper),
+ REG_JNI(register_com_google_android_gles_jni_EGLImpl),
+ REG_JNI(register_com_google_android_gles_jni_GLImpl),
+ REG_JNI(register_android_opengl_jni_EGL14),
+ REG_JNI(register_android_opengl_jni_EGL15),
+ REG_JNI(register_android_opengl_jni_EGLExt),
+ REG_JNI(register_android_opengl_jni_GLES10),
+ REG_JNI(register_android_opengl_jni_GLES10Ext),
+ REG_JNI(register_android_opengl_jni_GLES11),
+ REG_JNI(register_android_opengl_jni_GLES11Ext),
+ REG_JNI(register_android_opengl_jni_GLES20),
+ REG_JNI(register_android_opengl_jni_GLES30),
+ REG_JNI(register_android_opengl_jni_GLES31),
+ REG_JNI(register_android_opengl_jni_GLES31Ext),
+ REG_JNI(register_android_opengl_jni_GLES32),
+ REG_JNI(register_android_graphics_classes),
+ REG_JNI(register_android_graphics_BLASTBufferQueue),
+ REG_JNI(register_android_graphics_GraphicBuffer),
+ REG_JNI(register_android_database_CursorWindow),
+ REG_JNI(register_android_database_SQLiteConnection),
+ REG_JNI(register_android_database_SQLiteGlobal),
+ REG_JNI(register_android_database_SQLiteDebug),
+ REG_JNI(register_android_os_Debug),
+ REG_JNI(register_android_os_FileObserver),
+ REG_JNI(register_android_os_GraphicsEnvironment),
+ REG_JNI(register_android_os_MessageQueue),
+ REG_JNI(register_android_os_SELinux),
+ REG_JNI(register_android_os_Trace),
+ REG_JNI(register_android_os_UEventObserver),
+ REG_JNI(register_android_net_LocalSocketImpl),
+ REG_JNI(register_android_net_NetworkUtils),
+ REG_JNI(register_android_os_MemoryFile),
+ REG_JNI(register_android_os_SharedMemory),
+ REG_JNI(register_android_os_incremental_IncrementalManager),
+ REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
+ REG_JNI(register_com_android_internal_os_Zygote),
+ REG_JNI(register_com_android_internal_os_ZygoteInit),
+ REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
+ REG_JNI(register_android_hardware_Camera),
+ REG_JNI(register_android_hardware_camera2_CameraMetadata),
+ REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
+ REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),
+ REG_JNI(register_android_hardware_camera2_DngCreator),
+ REG_JNI(register_android_hardware_HardwareBuffer),
+ REG_JNI(register_android_hardware_SensorManager),
+ REG_JNI(register_android_hardware_SerialPort),
+ REG_JNI(register_android_hardware_UsbDevice),
+ REG_JNI(register_android_hardware_UsbDeviceConnection),
+ REG_JNI(register_android_hardware_UsbRequest),
+ REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
+ REG_JNI(register_android_media_AudioDevice),
+ REG_JNI(register_android_media_AudioEffectDescriptor),
+ REG_JNI(register_android_media_AudioSystem),
+ REG_JNI(register_android_media_AudioRecord),
+ REG_JNI(register_android_media_AudioTrack),
+ REG_JNI(register_android_media_AudioAttributes),
+ REG_JNI(register_android_media_AudioProductStrategies),
+ REG_JNI(register_android_media_AudioVolumeGroups),
+ REG_JNI(register_android_media_AudioVolumeGroupChangeHandler),
+ REG_JNI(register_android_media_MediaMetrics),
+ REG_JNI(register_android_media_MicrophoneInfo),
+ REG_JNI(register_android_media_RemoteDisplay),
+ REG_JNI(register_android_media_ToneGenerator),
+ REG_JNI(register_android_media_midi),
+
+ REG_JNI(register_android_opengl_classes),
+ REG_JNI(register_android_server_NetworkManagementSocketTagger),
+ REG_JNI(register_android_ddm_DdmHandleNativeHeap),
+ REG_JNI(register_android_backup_BackupDataInput),
+ REG_JNI(register_android_backup_BackupDataOutput),
+ REG_JNI(register_android_backup_FileBackupHelperBase),
+ REG_JNI(register_android_backup_BackupHelperDispatcher),
+ REG_JNI(register_android_app_backup_FullBackup),
+ REG_JNI(register_android_app_Activity),
+ REG_JNI(register_android_app_ActivityThread),
+ REG_JNI(register_android_app_NativeActivity),
+ REG_JNI(register_android_util_jar_StrictJarFile),
+ REG_JNI(register_android_view_InputChannel),
+ REG_JNI(register_android_view_InputEventReceiver),
+ REG_JNI(register_android_view_InputEventSender),
+ REG_JNI(register_android_view_InputQueue),
+ REG_JNI(register_android_view_KeyEvent),
+ REG_JNI(register_android_view_MotionEvent),
+ REG_JNI(register_android_view_PointerIcon),
+ REG_JNI(register_android_view_VelocityTracker),
+
+ REG_JNI(register_android_content_res_ObbScanner),
+ REG_JNI(register_android_content_res_Configuration),
+
+ REG_JNI(register_android_animation_PropertyValuesHolder),
+ REG_JNI(register_android_security_Scrypt),
+ REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
+ REG_JNI(register_com_android_internal_os_FuseAppLoop),
};
/*
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 8fc6afa0f386..2a56fd6d6d17 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -34,9 +34,9 @@
#include <hwui/MinikinSkia.h>
#include <hwui/Typeface.h>
-#include <utils/FatVector.h>
#include <minikin/FontFamily.h>
#include <minikin/LocaleList.h>
+#include <ui/FatVector.h>
#include <memory>
@@ -109,7 +109,7 @@ static jlong FontFamily_getFamilyReleaseFunc(CRITICAL_JNI_PARAMS) {
static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
jint weight, jint italic) {
- uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
+ FatVector<SkFontArguments::Axis, 2> skiaAxes;
for (const auto& axis : builder->axes) {
skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
}
diff --git a/core/jni/android/graphics/fonts/Font.cpp b/core/jni/android/graphics/fonts/Font.cpp
index bb0654d255c0..8d84e870d205 100644
--- a/core/jni/android/graphics/fonts/Font.cpp
+++ b/core/jni/android/graphics/fonts/Font.cpp
@@ -33,8 +33,8 @@
#include <hwui/MinikinSkia.h>
#include <hwui/Typeface.h>
-#include <utils/FatVector.h>
#include <minikin/FontFamily.h>
+#include <ui/FatVector.h>
#include <memory>
@@ -157,7 +157,7 @@ static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jo
sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
release_global_ref, reinterpret_cast<void*>(fontRef)));
- uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
+ FatVector<SkFontArguments::Axis, 2> skiaAxes;
for (const auto& axis : builder->axes) {
skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
}
diff --git a/core/jni/android_media_AudioDeviceAddress.cpp b/core/jni/android_media_AudioDevice.cpp
index 5f39f7efb6a2..f6a0e4be1066 100644
--- a/core/jni/android_media_AudioDeviceAddress.cpp
+++ b/core/jni/android_media_AudioDevice.cpp
@@ -14,40 +14,38 @@
* limitations under the License.
*/
-#include "core_jni_helpers.h"
-#include "android_media_AudioDeviceAddress.h"
+#include "android_media_AudioDevice.h"
#include "android_media_AudioErrors.h"
+#include "core_jni_helpers.h"
#include <media/AudioDeviceTypeAddr.h>
using namespace android;
-static jclass gAudioDeviceAddressClass;
-static jmethodID gAudioDeviceAddressCstor;
+static jclass gAudioDeviceClass;
+static jmethodID gAudioDeviceCstor;
namespace android {
-jint createAudioDeviceAddressFromNative(
- JNIEnv *env, jobject *jAudioDeviceAddress,
- const AudioDeviceTypeAddr *devTypeAddr) {
+jint createAudioDeviceFromNative(JNIEnv *env, jobject *jAudioDevice,
+ const AudioDeviceTypeAddr *devTypeAddr) {
jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
jint jNativeType = (jint)devTypeAddr->mType;
ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->mAddress.data()));
- *jAudioDeviceAddress = env->NewObject(gAudioDeviceAddressClass, gAudioDeviceAddressCstor,
- jNativeType, jAddress.get());
+ *jAudioDevice =
+ env->NewObject(gAudioDeviceClass, gAudioDeviceCstor, jNativeType, jAddress.get());
return jStatus;
}
-}
+} // namespace android
-int register_android_media_AudioDeviceAddress(JNIEnv *env)
-{
- jclass audioDeviceTypeAddressClass = FindClassOrDie(env, "android/media/AudioDeviceAddress");
- gAudioDeviceAddressClass = MakeGlobalRefOrDie(env, audioDeviceTypeAddressClass);
- gAudioDeviceAddressCstor = GetMethodIDOrDie(env, audioDeviceTypeAddressClass, "<init>",
- "(ILjava/lang/String;)V");
+int register_android_media_AudioDevice(JNIEnv *env) {
+ jclass audioDeviceTypeAddressClass = FindClassOrDie(env, "android/media/AudioDevice");
+ gAudioDeviceClass = MakeGlobalRefOrDie(env, audioDeviceTypeAddressClass);
+ gAudioDeviceCstor =
+ GetMethodIDOrDie(env, audioDeviceTypeAddressClass, "<init>", "(ILjava/lang/String;)V");
return 0;
}
diff --git a/core/jni/android_media_AudioDeviceAddress.h b/core/jni/android_media_AudioDevice.h
index c66b17978776..fc92334db194 100644
--- a/core/jni/android_media_AudioDeviceAddress.h
+++ b/core/jni/android_media_AudioDevice.h
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-#ifndef ANDROID_MEDIA_AUDIODEVICEADDRESS_H
-#define ANDROID_MEDIA_AUDIODEVICEADDRESS_H
+#ifndef ANDROID_MEDIA_AUDIODEVICE_H
+#define ANDROID_MEDIA_AUDIODEVICE_H
-#include <system/audio.h>
#include <media/AudioDeviceTypeAddr.h>
+#include <system/audio.h>
#include "jni.h"
namespace android {
-// Create a Java AudioDeviceAddress instance from a C++ AudioDeviceTypeAddress
+// Create a Java AudioDevice instance from a C++ AudioDeviceTypeAddress
-extern jint createAudioDeviceAddressFromNative(JNIEnv *env, jobject *jAudioDeviceAddress,
- const AudioDeviceTypeAddr *devTypeAddr);
+extern jint createAudioDeviceFromNative(JNIEnv *env, jobject *jAudioDevice,
+ const AudioDeviceTypeAddr *devTypeAddr);
} // namespace android
#endif \ No newline at end of file
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 0156e23e94b6..b4590f4f8b23 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -26,12 +26,6 @@
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
-#include "android_media_AudioAttributes.h"
-#include "android_media_AudioDeviceAddress.h"
-#include "android_media_AudioEffectDescriptor.h"
-#include "android_media_AudioErrors.h"
-#include "android_media_AudioFormat.h"
-#include "android_media_MicrophoneInfo.h"
#include <audiomanager/AudioManager.h>
#include <media/AudioPolicy.h>
#include <media/AudioSystem.h>
@@ -39,6 +33,12 @@
#include <nativehelper/ScopedLocalRef.h>
#include <system/audio.h>
#include <system/audio_policy.h>
+#include "android_media_AudioAttributes.h"
+#include "android_media_AudioDevice.h"
+#include "android_media_AudioEffectDescriptor.h"
+#include "android_media_AudioErrors.h"
+#include "android_media_AudioFormat.h"
+#include "android_media_MicrophoneInfo.h"
// ----------------------------------------------------------------------------
@@ -2349,7 +2349,7 @@ android_media_AudioSystem_getPreferredDeviceForStrategy(JNIEnv *env, jobject thi
jint strategy, jobjectArray jDeviceArray)
{
if (jDeviceArray == nullptr || env->GetArrayLength(jDeviceArray) != 1) {
- ALOGE("%s invalid array to store AudioDeviceAddress", __FUNCTION__);
+ ALOGE("%s invalid array to store AudioDevice", __FUNCTION__);
return (jint)AUDIO_JAVA_BAD_VALUE;
}
@@ -2359,10 +2359,10 @@ android_media_AudioSystem_getPreferredDeviceForStrategy(JNIEnv *env, jobject thi
if (status != NO_ERROR) {
return (jint) status;
}
- jobject jAudioDeviceAddress = NULL;
- jint jStatus = createAudioDeviceAddressFromNative(env, &jAudioDeviceAddress, &elDevice);
+ jobject jAudioDevice = NULL;
+ jint jStatus = createAudioDeviceFromNative(env, &jAudioDevice, &elDevice);
if (jStatus == AUDIO_JAVA_SUCCESS) {
- env->SetObjectArrayElement(jDeviceArray, 0, jAudioDeviceAddress);
+ env->SetObjectArrayElement(jDeviceArray, 0, jAudioDevice);
}
return jStatus;
}
@@ -2377,7 +2377,7 @@ android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
// with reverse JNI to make the array grow as need as this would be less efficient, and some
// components call this method often
if (jDeviceArray == nullptr || maxResultSize == 0) {
- ALOGE("%s invalid array to store AudioDeviceAddress", __FUNCTION__);
+ ALOGE("%s invalid array to store AudioDevice", __FUNCTION__);
return (jint)AUDIO_JAVA_BAD_VALUE;
}
@@ -2398,105 +2398,133 @@ android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
return AUDIO_JAVA_INVALID_OPERATION;
}
size_t index = 0;
- jobject jAudioDeviceAddress = NULL;
+ jobject jAudioDevice = NULL;
for (const auto& device : devices) {
- jStatus = createAudioDeviceAddressFromNative(env, &jAudioDeviceAddress, &device);
+ jStatus = createAudioDeviceFromNative(env, &jAudioDevice, &device);
if (jStatus != AUDIO_JAVA_SUCCESS) {
return jStatus;
}
- env->SetObjectArrayElement(jDeviceArray, index++, jAudioDeviceAddress);
+ env->SetObjectArrayElement(jDeviceArray, index++, jAudioDevice);
}
return jStatus;
}
// ----------------------------------------------------------------------------
-static const JNINativeMethod gMethods[] = {
- {"setParameters", "(Ljava/lang/String;)I", (void *)android_media_AudioSystem_setParameters},
- {"getParameters", "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_AudioSystem_getParameters},
- {"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone},
- {"isMicrophoneMuted", "()Z", (void *)android_media_AudioSystem_isMicrophoneMuted},
- {"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive},
- {"isStreamActiveRemotely","(II)Z", (void *)android_media_AudioSystem_isStreamActiveRemotely},
- {"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive},
- {"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId},
- {"newAudioPlayerId", "()I", (void *)android_media_AudioSystem_newAudioPlayerId},
- {"newAudioRecorderId", "()I", (void *)android_media_AudioSystem_newAudioRecorderId},
- {"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;I)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
- {"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState},
- {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;I)I", (void *)android_media_AudioSystem_handleDeviceConfigChange},
- {"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState},
- {"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse},
- {"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse},
- {"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume},
- {"setStreamVolumeIndex","(III)I", (void *)android_media_AudioSystem_setStreamVolumeIndex},
- {"getStreamVolumeIndex","(II)I", (void *)android_media_AudioSystem_getStreamVolumeIndex},
- {"setVolumeIndexForAttributes","(Landroid/media/AudioAttributes;II)I", (void *)android_media_AudioSystem_setVolumeIndexForAttributes},
- {"getVolumeIndexForAttributes","(Landroid/media/AudioAttributes;I)I", (void *)android_media_AudioSystem_getVolumeIndexForAttributes},
- {"getMinVolumeIndexForAttributes","(Landroid/media/AudioAttributes;)I", (void *)android_media_AudioSystem_getMinVolumeIndexForAttributes},
- {"getMaxVolumeIndexForAttributes","(Landroid/media/AudioAttributes;)I", (void *)android_media_AudioSystem_getMaxVolumeIndexForAttributes},
- {"setMasterVolume", "(F)I", (void *)android_media_AudioSystem_setMasterVolume},
- {"getMasterVolume", "()F", (void *)android_media_AudioSystem_getMasterVolume},
- {"setMasterMute", "(Z)I", (void *)android_media_AudioSystem_setMasterMute},
- {"getMasterMute", "()Z", (void *)android_media_AudioSystem_getMasterMute},
- {"setMasterMono", "(Z)I", (void *)android_media_AudioSystem_setMasterMono},
- {"getMasterMono", "()Z", (void *)android_media_AudioSystem_getMasterMono},
- {"setMasterBalance", "(F)I", (void *)android_media_AudioSystem_setMasterBalance},
- {"getMasterBalance", "()F", (void *)android_media_AudioSystem_getMasterBalance},
- {"getDevicesForStream", "(I)I", (void *)android_media_AudioSystem_getDevicesForStream},
- {"getPrimaryOutputSamplingRate", "()I", (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate},
- {"getPrimaryOutputFrameCount", "()I", (void *)android_media_AudioSystem_getPrimaryOutputFrameCount},
- {"getOutputLatency", "(I)I", (void *)android_media_AudioSystem_getOutputLatency},
- {"setLowRamDevice", "(ZJ)I", (void *)android_media_AudioSystem_setLowRamDevice},
- {"checkAudioFlinger", "()I", (void *)android_media_AudioSystem_checkAudioFlinger},
- {"listAudioPorts", "(Ljava/util/ArrayList;[I)I",
- (void *)android_media_AudioSystem_listAudioPorts},
- {"createAudioPatch", "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)I",
- (void *)android_media_AudioSystem_createAudioPatch},
- {"releaseAudioPatch", "(Landroid/media/AudioPatch;)I",
- (void *)android_media_AudioSystem_releaseAudioPatch},
- {"listAudioPatches", "(Ljava/util/ArrayList;[I)I",
- (void *)android_media_AudioSystem_listAudioPatches},
- {"setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I",
- (void *)android_media_AudioSystem_setAudioPortConfig},
- {"startAudioSource", "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I",
- (void *)android_media_AudioSystem_startAudioSource},
- {"stopAudioSource", "(I)I", (void *)android_media_AudioSystem_stopAudioSource},
- {"getAudioHwSyncForSession", "(I)I",
- (void *)android_media_AudioSystem_getAudioHwSyncForSession},
- {"registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
- (void *)android_media_AudioSystem_registerPolicyMixes},
- {"setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I",
- (void *)android_media_AudioSystem_setUidDeviceAffinities},
- {"removeUidDeviceAffinities", "(I)I",
- (void *)android_media_AudioSystem_removeUidDeviceAffinities},
- {"native_register_dynamic_policy_callback", "()V",
- (void *)android_media_AudioSystem_registerDynPolicyCallback},
- {"native_register_recording_callback", "()V",
- (void *)android_media_AudioSystem_registerRecordingCallback},
- {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
- {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
- {"native_is_offload_supported", "(IIIII)Z", (void *)android_media_AudioSystem_isOffloadSupported},
- {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones},
- {"getSurroundFormats", "(Ljava/util/Map;Z)I", (void *)android_media_AudioSystem_getSurroundFormats},
- {"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled},
- {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
- {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
- {"isHapticPlaybackSupported", "()Z", (void *)android_media_AudioSystem_isHapticPlaybackSupported},
- {"getHwOffloadEncodingFormatsSupportedForA2DP", "(Ljava/util/ArrayList;)I",
- (void*)android_media_AudioSystem_getHwOffloadEncodingFormatsSupportedForA2DP},
- {"setSupportedSystemUsages", "([I)I", (void *)android_media_AudioSystem_setSupportedSystemUsages},
- {"setAllowedCapturePolicy", "(II)I", (void *)android_media_AudioSystem_setAllowedCapturePolicy},
- {"setRttEnabled", "(Z)I", (void *)android_media_AudioSystem_setRttEnabled},
- {"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids},
- {"isCallScreeningModeSupported", "()Z", (void *)android_media_AudioSystem_isCallScreeningModeSupported},
- {"setPreferredDeviceForStrategy", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setPreferredDeviceForStrategy},
- {"removePreferredDeviceForStrategy", "(I)I", (void *)android_media_AudioSystem_removePreferredDeviceForStrategy},
- {"getPreferredDeviceForStrategy", "(I[Landroid/media/AudioDeviceAddress;)I", (void *)android_media_AudioSystem_getPreferredDeviceForStrategy},
- {"getDevicesForAttributes", "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAddress;)I", (void *)android_media_AudioSystem_getDevicesForAttributes},
- {"setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I", (void *)android_media_AudioSystem_setUserIdDeviceAffinities},
- {"removeUserIdDeviceAffinities", "(I)I", (void *)android_media_AudioSystem_removeUserIdDeviceAffinities}
-};
+static const JNINativeMethod gMethods[] =
+ {{"setParameters", "(Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_setParameters},
+ {"getParameters", "(Ljava/lang/String;)Ljava/lang/String;",
+ (void *)android_media_AudioSystem_getParameters},
+ {"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone},
+ {"isMicrophoneMuted", "()Z", (void *)android_media_AudioSystem_isMicrophoneMuted},
+ {"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive},
+ {"isStreamActiveRemotely", "(II)Z",
+ (void *)android_media_AudioSystem_isStreamActiveRemotely},
+ {"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive},
+ {"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId},
+ {"newAudioPlayerId", "()I", (void *)android_media_AudioSystem_newAudioPlayerId},
+ {"newAudioRecorderId", "()I", (void *)android_media_AudioSystem_newAudioRecorderId},
+ {"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;I)I",
+ (void *)android_media_AudioSystem_setDeviceConnectionState},
+ {"getDeviceConnectionState", "(ILjava/lang/String;)I",
+ (void *)android_media_AudioSystem_getDeviceConnectionState},
+ {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;I)I",
+ (void *)android_media_AudioSystem_handleDeviceConfigChange},
+ {"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState},
+ {"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse},
+ {"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse},
+ {"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume},
+ {"setStreamVolumeIndex", "(III)I", (void *)android_media_AudioSystem_setStreamVolumeIndex},
+ {"getStreamVolumeIndex", "(II)I", (void *)android_media_AudioSystem_getStreamVolumeIndex},
+ {"setVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;II)I",
+ (void *)android_media_AudioSystem_setVolumeIndexForAttributes},
+ {"getVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;I)I",
+ (void *)android_media_AudioSystem_getVolumeIndexForAttributes},
+ {"getMinVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;)I",
+ (void *)android_media_AudioSystem_getMinVolumeIndexForAttributes},
+ {"getMaxVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;)I",
+ (void *)android_media_AudioSystem_getMaxVolumeIndexForAttributes},
+ {"setMasterVolume", "(F)I", (void *)android_media_AudioSystem_setMasterVolume},
+ {"getMasterVolume", "()F", (void *)android_media_AudioSystem_getMasterVolume},
+ {"setMasterMute", "(Z)I", (void *)android_media_AudioSystem_setMasterMute},
+ {"getMasterMute", "()Z", (void *)android_media_AudioSystem_getMasterMute},
+ {"setMasterMono", "(Z)I", (void *)android_media_AudioSystem_setMasterMono},
+ {"getMasterMono", "()Z", (void *)android_media_AudioSystem_getMasterMono},
+ {"setMasterBalance", "(F)I", (void *)android_media_AudioSystem_setMasterBalance},
+ {"getMasterBalance", "()F", (void *)android_media_AudioSystem_getMasterBalance},
+ {"getDevicesForStream", "(I)I", (void *)android_media_AudioSystem_getDevicesForStream},
+ {"getPrimaryOutputSamplingRate", "()I",
+ (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate},
+ {"getPrimaryOutputFrameCount", "()I",
+ (void *)android_media_AudioSystem_getPrimaryOutputFrameCount},
+ {"getOutputLatency", "(I)I", (void *)android_media_AudioSystem_getOutputLatency},
+ {"setLowRamDevice", "(ZJ)I", (void *)android_media_AudioSystem_setLowRamDevice},
+ {"checkAudioFlinger", "()I", (void *)android_media_AudioSystem_checkAudioFlinger},
+ {"listAudioPorts", "(Ljava/util/ArrayList;[I)I",
+ (void *)android_media_AudioSystem_listAudioPorts},
+ {"createAudioPatch",
+ "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/"
+ "AudioPortConfig;)I",
+ (void *)android_media_AudioSystem_createAudioPatch},
+ {"releaseAudioPatch", "(Landroid/media/AudioPatch;)I",
+ (void *)android_media_AudioSystem_releaseAudioPatch},
+ {"listAudioPatches", "(Ljava/util/ArrayList;[I)I",
+ (void *)android_media_AudioSystem_listAudioPatches},
+ {"setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I",
+ (void *)android_media_AudioSystem_setAudioPortConfig},
+ {"startAudioSource", "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I",
+ (void *)android_media_AudioSystem_startAudioSource},
+ {"stopAudioSource", "(I)I", (void *)android_media_AudioSystem_stopAudioSource},
+ {"getAudioHwSyncForSession", "(I)I",
+ (void *)android_media_AudioSystem_getAudioHwSyncForSession},
+ {"registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
+ (void *)android_media_AudioSystem_registerPolicyMixes},
+ {"setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_setUidDeviceAffinities},
+ {"removeUidDeviceAffinities", "(I)I",
+ (void *)android_media_AudioSystem_removeUidDeviceAffinities},
+ {"native_register_dynamic_policy_callback", "()V",
+ (void *)android_media_AudioSystem_registerDynPolicyCallback},
+ {"native_register_recording_callback", "()V",
+ (void *)android_media_AudioSystem_registerRecordingCallback},
+ {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
+ {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
+ {"native_is_offload_supported", "(IIIII)Z",
+ (void *)android_media_AudioSystem_isOffloadSupported},
+ {"getMicrophones", "(Ljava/util/ArrayList;)I",
+ (void *)android_media_AudioSystem_getMicrophones},
+ {"getSurroundFormats", "(Ljava/util/Map;Z)I",
+ (void *)android_media_AudioSystem_getSurroundFormats},
+ {"setSurroundFormatEnabled", "(IZ)I",
+ (void *)android_media_AudioSystem_setSurroundFormatEnabled},
+ {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
+ {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
+ {"isHapticPlaybackSupported", "()Z",
+ (void *)android_media_AudioSystem_isHapticPlaybackSupported},
+ {"getHwOffloadEncodingFormatsSupportedForA2DP", "(Ljava/util/ArrayList;)I",
+ (void *)android_media_AudioSystem_getHwOffloadEncodingFormatsSupportedForA2DP},
+ {"setSupportedSystemUsages", "([I)I",
+ (void *)android_media_AudioSystem_setSupportedSystemUsages},
+ {"setAllowedCapturePolicy", "(II)I",
+ (void *)android_media_AudioSystem_setAllowedCapturePolicy},
+ {"setRttEnabled", "(Z)I", (void *)android_media_AudioSystem_setRttEnabled},
+ {"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids},
+ {"isCallScreeningModeSupported", "()Z",
+ (void *)android_media_AudioSystem_isCallScreeningModeSupported},
+ {"setPreferredDeviceForStrategy", "(IILjava/lang/String;)I",
+ (void *)android_media_AudioSystem_setPreferredDeviceForStrategy},
+ {"removePreferredDeviceForStrategy", "(I)I",
+ (void *)android_media_AudioSystem_removePreferredDeviceForStrategy},
+ {"getPreferredDeviceForStrategy", "(I[Landroid/media/AudioDevice;)I",
+ (void *)android_media_AudioSystem_getPreferredDeviceForStrategy},
+ {"getDevicesForAttributes",
+ "(Landroid/media/AudioAttributes;[Landroid/media/AudioDevice;)I",
+ (void *)android_media_AudioSystem_getDevicesForAttributes},
+ {"setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_setUserIdDeviceAffinities},
+ {"removeUserIdDeviceAffinities", "(I)I",
+ (void *)android_media_AudioSystem_removeUserIdDeviceAffinities}};
static const JNINativeMethod gEventHandlerMethods[] = {
{"native_setup",
diff --git a/core/jni/android_os_incremental_IncrementalManager.cpp b/core/jni/android_os_incremental_IncrementalManager.cpp
index 698062a2651e..d41e98241b07 100644
--- a/core/jni/android_os_incremental_IncrementalManager.cpp
+++ b/core/jni/android_os_incremental_IncrementalManager.cpp
@@ -26,6 +26,10 @@
namespace android {
+static jboolean nativeIsEnabled(JNIEnv* env, jobject clazz) {
+ return IncFs_IsEnabled();
+}
+
static jboolean nativeIsIncrementalPath(JNIEnv* env,
jobject clazz,
jstring javaPath) {
@@ -34,8 +38,8 @@ static jboolean nativeIsIncrementalPath(JNIEnv* env,
}
static const JNINativeMethod method_table[] = {
- {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z",
- (void*)nativeIsIncrementalPath},
+ {"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
+ {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z", (void*)nativeIsIncrementalPath},
};
int register_android_os_incremental_IncrementalManager(JNIEnv* env) {
diff --git a/core/proto/android/app/appexitinfo.proto b/core/proto/android/app/appexitinfo.proto
index e23f150fab25..6a4922000805 100644
--- a/core/proto/android/app/appexitinfo.proto
+++ b/core/proto/android/app/appexitinfo.proto
@@ -178,8 +178,8 @@ message ApplicationExitInfoProto {
}
optional Importance importance = 10;
- optional int32 pss = 11;
- optional int32 rss = 12;
+ optional int64 pss = 11;
+ optional int64 rss = 12;
optional int64 timestamp = 13;
optional string description = 14;
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index fc0a2ef6041a..c14d99c5d3fb 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2578,4 +2578,7 @@ enum PageId {
// CATEGORY: SETTINGS
// OS: R
CONNECTION_DEVICE_ADVANCED_NFC = 1828;
+
+ // OPEN: Settings -> Apps & Notifications -> Special App Access
+ INTERACT_ACROSS_PROFILES = 1829;
}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 303d62dbb30a..546c5a092a50 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -219,12 +219,8 @@ message ConstantsProto {
// The maximum number of background jobs we allow when the system is in a
// critical memory state.
optional int32 bg_critical_job_count = 14;
- // The maximum number of times we allow a job to have itself rescheduled
- // before giving up on it, for standard jobs.
- optional int32 max_standard_reschedule_count = 15;
- // The maximum number of times we allow a job to have itself rescheduled
- // before giving up on it, for jobs that are executing work.
- optional int32 max_work_reschedule_count = 16;
+ reserved 15; // max_standard_reschedule_count
+ reserved 16; // max_work_reschedule_count
// The minimum backoff time to allow for linear backoff.
optional int64 min_linear_backoff_time_ms = 17;
// The minimum backoff time to allow for exponential backoff.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index de0d172ecf04..ff696715e94d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1535,7 +1535,7 @@
@hide
-->
<permission android:name="android.permission.ACCESS_CONTEXT_HUB"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.ACCESS_CONTEXT_HUB"/>
<!-- @SystemApi Allows an application to create mock location providers for testing.
@@ -1998,17 +1998,17 @@
<permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows TvInputService to access underlying TV input hardware such as
+ <!-- Allows TvInputService to access underlying TV input hardware such as
built-in tuners and HDMI-in's.
- @hide This should only be used by OEM's TvInputService's.
- -->
+ <p>This should only be used by OEM's TvInputService's.
+ @hide @SystemApi -->
<permission android:name="android.permission.TV_INPUT_HARDWARE"
android:protectionLevel="signature|privileged|vendorPrivileged" />
- <!-- @SystemApi Allows to capture a frame of TV input hardware such as
+ <!-- Allows to capture a frame of TV input hardware such as
built-in tuners and HDMI-in's.
- @hide <p>Not for use by third-party applications.
- -->
+ <p>Not for use by third-party applications.
+ @hide @SystemApi -->
<permission android:name="android.permission.CAPTURE_TV_INPUT"
android:protectionLevel="signature|privileged" />
@@ -3433,10 +3433,10 @@
<permission android:name="android.permission.READ_CONTENT_RATING_SYSTEMS"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to notify TV inputs by sending broadcasts.
+ <!-- Allows an application to notify TV inputs by sending broadcasts.
<p>Protection level: signature|privileged
<p>Not for use by third-party applications.
- @hide -->
+ @hide @SystemApi -->
<permission android:name="android.permission.NOTIFY_TV_INPUTS"
android:protectionLevel="signature|privileged" />
diff --git a/core/res/res/drawable/tab_indicator_resolver.xml b/core/res/res/drawable/tab_indicator_resolver.xml
new file mode 100644
index 000000000000..ff16d81a6383
--- /dev/null
+++ b/core/res/res/drawable/tab_indicator_resolver.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingMode="nest">
+ <item>
+ <ripple android:color="@color/tab_highlight_material">
+ <item android:id="@id/mask">
+ <color android:color="@color/white" />
+ </item>
+ </ripple>
+ </item>
+ <item android:gravity="bottom">
+ <shape android:shape="rectangle"
+ android:tint="@color/resolver_tabs_active_color">
+ <size android:height="2dp" />
+ <solid android:color="@color/tab_indicator_material" />
+ </shape>
+ </item>
+ <item android:bottom="2dp"
+ android:drawable="@color/transparent" />
+</layer-list>
diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml
index d6fd7aa30e6e..d19e313055ae 100644
--- a/core/res/res/layout/accessibility_button_chooser_item.xml
+++ b/core/res/res/layout/accessibility_button_chooser_item.xml
@@ -20,7 +20,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:clickable="true"
android:gravity="center"
android:paddingStart="16dp"
android:paddingEnd="16dp"
diff --git a/core/res/res/layout/tab_indicator_resolver.xml b/core/res/res/layout/tab_indicator_resolver.xml
new file mode 100644
index 000000000000..2038da656ee3
--- /dev/null
+++ b/core/res/res/layout/tab_indicator_resolver.xml
@@ -0,0 +1,38 @@
+<?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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="?android:attr/actionBarSize"
+ android:orientation="horizontal"
+ style="@android:style/Widget.Material.Resolver.Tab">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAllCaps="false"
+ style="@android:style/Widget.Material.TabText" />
+
+</LinearLayout>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index c5e72f012a21..33caeb65c2f8 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -32,4 +32,6 @@
<color name="chooser_row_divider">@color/list_divider_color_dark</color>
<color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color>
<color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color>
+
+ <color name="resolver_tabs_active_color">#FF8AB4F8</color>
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index cef21db1e0f8..81ec27841aa7 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1679,6 +1679,15 @@ easier.
<item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
<item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
<item name="navigationBarColor">@android:color/transparent</item>
+ <item name="tabWidgetStyle">@style/Widget.DeviceDefault.Resolver.TabWidget</item>
+ </style>
+
+ <style name="Widget.DeviceDefault.Resolver.TabWidget" parent="Widget.DeviceDefault.TabWidget">
+ <item name="tabLayout">@layout/tab_indicator_resolver</item>
+ </style>
+
+ <style name="Widget.Material.Resolver.Tab" parent="Widget.Material.Tab">
+ <item name="background">@drawable/tab_indicator_resolver</item>
</style>
<style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon">
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 8b8e9ea4c6ee..b71c5800161e 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -19,7 +19,6 @@ package android.view.accessibility;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.content.pm.ParceledListSlice;
-import android.graphics.Bitmap;
import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
@@ -157,9 +156,5 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
return -1;
}
- public Bitmap takeScreenshot(int displayId) {
- return null;
- }
-
- public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
+ public void takeScreenshot(int displayId, RemoteCallback callback) {}
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 6761435a8171..31e45558139d 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -27,7 +27,6 @@
#include "DamageAccumulator.h"
#include "pipeline/skia/SkiaDisplayList.h"
#endif
-#include "utils/FatVector.h"
#include "utils/MathUtils.h"
#include "utils/StringUtils.h"
#include "utils/TraceUtils.h"
@@ -37,6 +36,7 @@
#include <atomic>
#include <sstream>
#include <string>
+#include <ui/FatVector.h>
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index d55e5b0ce836..c0ec2174bb35 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -27,6 +27,8 @@
#include <androidfw/ResourceTypes.h>
+#include <ui/FatVector.h>
+
#include "AnimatorManager.h"
#include "CanvasTransform.h"
#include "Debug.h"
@@ -35,7 +37,6 @@
#include "RenderProperties.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "pipeline/skia/SkiaLayer.h"
-#include "utils/FatVector.h"
#include <vector>
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index 4b2857f6c290..afd82aca07c5 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -24,6 +24,19 @@
using namespace android;
+sk_sp<SkColorSpace> ImageDecoder::getDefaultColorSpace() const {
+ const skcms_ICCProfile* encodedProfile = mCodec->getICCProfile();
+ if (encodedProfile) {
+ // If the profile maps directly to an SkColorSpace, that SkColorSpace
+ // will be returned. Otherwise, nullptr will be returned. In either
+ // case, using this SkColorSpace results in doing no color correction.
+ return SkColorSpace::Make(*encodedProfile);
+ }
+
+ // The image has no embedded color profile, and should be treated as SRGB.
+ return SkColorSpace::MakeSRGB();
+}
+
ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChunkReader> peeker)
: mCodec(std::move(codec))
, mPeeker(std::move(peeker))
@@ -31,7 +44,7 @@ ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChu
, mDecodeSize(mTargetSize)
, mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType))
, mUnpremultipliedRequired(false)
- , mOutColorSpace(mCodec->computeOutputColorSpace(mOutColorType, nullptr))
+ , mOutColorSpace(getDefaultColorSpace())
, mSampleSize(1)
{
}
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
index 0c99f84cbb72..a1b51573db3f 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -43,6 +43,7 @@ public:
bool setUnpremultipliedRequired(bool unpremultipliedRequired);
+ sk_sp<SkColorSpace> getDefaultColorSpace() const;
void setOutColorSpace(sk_sp<SkColorSpace> cs);
// The size is the final size after scaling and cropping.
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
index cfc0f9b258da..d669f84c5ee2 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -21,7 +21,7 @@
#include <SkCanvas.h>
#include <SkDrawable.h>
-#include <utils/FatVector.h>
+#include <ui/FatVector.h>
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index cae3e3b5188c..206b58f62ea7 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -27,7 +27,6 @@
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
#include "renderstate/RenderState.h"
-#include "utils/FatVector.h"
#include "utils/TimeUtils.h"
#include "utils/TraceUtils.h"
@@ -40,6 +39,8 @@
#include <utils/Mutex.h>
#include <thread>
+#include <ui/FatVector.h>
+
namespace android {
namespace uirenderer {
namespace renderthread {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index a5355fc3499d..ba70afc8b8d2 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -23,13 +23,13 @@
#include <GrContext.h>
#include <GrTypes.h>
#include <android/sync.h>
+#include <ui/FatVector.h>
#include <vk/GrVkExtensions.h>
#include <vk/GrVkTypes.h>
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
-#include "utils/FatVector.h"
#include "utils/TraceUtils.h"
namespace android {
diff --git a/libs/hwui/tests/unit/FatVectorTests.cpp b/libs/hwui/tests/unit/FatVectorTests.cpp
index 8523e6c9e973..6585a6249b44 100644
--- a/libs/hwui/tests/unit/FatVectorTests.cpp
+++ b/libs/hwui/tests/unit/FatVectorTests.cpp
@@ -15,7 +15,7 @@
*/
#include <gtest/gtest.h>
-#include <utils/FatVector.h>
+#include <ui/FatVector.h>
#include <tests/common/TestUtils.h>
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
deleted file mode 100644
index 8cc4d1010ab6..000000000000
--- a/libs/hwui/utils/FatVector.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2015, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef ANDROID_FAT_VECTOR_H
-#define ANDROID_FAT_VECTOR_H
-
-#include "utils/Macros.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <utils/Log.h>
-#include <type_traits>
-
-#include <vector>
-
-namespace android {
-namespace uirenderer {
-
-template <typename T, size_t SIZE>
-class InlineStdAllocator {
-public:
- struct Allocation {
- PREVENT_COPY_AND_ASSIGN(Allocation);
-
- public:
- Allocation(){};
- // char array instead of T array, so memory is uninitialized, with no destructors run
- char array[sizeof(T) * SIZE];
- bool inUse = false;
- };
-
- typedef T value_type; // needed to implement std::allocator
- typedef T* pointer; // needed to implement std::allocator
-
- explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {}
- InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {}
- ~InlineStdAllocator() {}
-
- T* allocate(size_t num, const void* = 0) {
- if (!mAllocation.inUse && num <= SIZE) {
- mAllocation.inUse = true;
- return (T*)mAllocation.array;
- } else {
- return (T*)malloc(num * sizeof(T));
- }
- }
-
- void deallocate(pointer p, size_t num) {
- if (p == (T*)mAllocation.array) {
- mAllocation.inUse = false;
- } else {
- // 'free' instead of delete here - destruction handled separately
- free(p);
- }
- }
- Allocation& mAllocation;
-};
-
-/**
- * std::vector with SIZE elements preallocated into an internal buffer.
- *
- * Useful for avoiding the cost of malloc in cases where only SIZE or
- * fewer elements are needed in the common case.
- */
-template <typename T, size_t SIZE>
-class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
-public:
- FatVector()
- : std::vector<T, InlineStdAllocator<T, SIZE>>(
- InlineStdAllocator<T, SIZE>(mAllocation)) {
- this->reserve(SIZE);
- }
-
- explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); }
-
-private:
- typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
-};
-
-} // namespace uirenderer
-} // namespace android
-
-#endif // ANDROID_FAT_VECTOR_H
diff --git a/media/java/android/media/AudioDeviceAddress.aidl b/media/java/android/media/AudioDevice.aidl
index 6a1a7f79247c..02071e5f0c13 100644
--- a/media/java/android/media/AudioDeviceAddress.aidl
+++ b/media/java/android/media/AudioDevice.aidl
@@ -15,4 +15,4 @@
package android.media;
-parcelable AudioDeviceAddress;
+parcelable AudioDevice;
diff --git a/media/java/android/media/AudioDeviceAddress.java b/media/java/android/media/AudioDevice.java
index 3d8fc373006e..31ecc7b4ead4 100644
--- a/media/java/android/media/AudioDeviceAddress.java
+++ b/media/java/android/media/AudioDevice.java
@@ -28,7 +28,7 @@ import java.util.Objects;
/**
* @hide
- * Class to represent device type (speaker, headset...), address and role (input, output)
+ * Class to represent device type (speaker, headset...), address (if known) and role (input, output)
* of an audio device.
* <p>Unlike {@link AudioDeviceInfo}, the device
* doesn't need to be connected to be uniquely identified, it can
@@ -39,7 +39,7 @@ import java.util.Objects;
* permission, APIs using one rely on MODIFY_AUDIO_ROUTING.
*/
@SystemApi
-public final class AudioDeviceAddress implements Parcelable {
+public final class AudioDevice implements Parcelable {
/**
* A role identifying input devices, such as microphones.
@@ -78,7 +78,7 @@ public final class AudioDeviceAddress implements Parcelable {
* type and address.
*/
@SystemApi
- public AudioDeviceAddress(@NonNull AudioDeviceInfo deviceInfo) {
+ public AudioDevice(@NonNull AudioDeviceInfo deviceInfo) {
Objects.requireNonNull(deviceInfo);
mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT;
mType = deviceInfo.getType();
@@ -93,7 +93,7 @@ public final class AudioDeviceAddress implements Parcelable {
* @param address the address of the device, or an empty string for devices without one
*/
@SystemApi
- public AudioDeviceAddress(@Role int role, @AudioDeviceInfo.AudioDeviceType int type,
+ public AudioDevice(@Role int role, @AudioDeviceInfo.AudioDeviceType int type,
@NonNull String address) {
Objects.requireNonNull(address);
if (role != ROLE_OUTPUT && role != ROLE_INPUT) {
@@ -111,7 +111,7 @@ public final class AudioDeviceAddress implements Parcelable {
mAddress = address;
}
- /*package*/ AudioDeviceAddress(int nativeType, @NonNull String address) {
+ /*package*/ AudioDevice(int nativeType, @NonNull String address) {
mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT;
mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
mAddress = address;
@@ -157,7 +157,7 @@ public final class AudioDeviceAddress implements Parcelable {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- AudioDeviceAddress that = (AudioDeviceAddress) o;
+ AudioDevice that = (AudioDevice) o;
return ((mRole == that.mRole)
&& (mType == that.mType)
&& mAddress.equals(that.mAddress));
@@ -170,7 +170,7 @@ public final class AudioDeviceAddress implements Parcelable {
@Override
public String toString() {
- return new String("AudioDeviceAddress:"
+ return new String("AudioDevice:"
+ " role:" + roleToString(mRole)
+ " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(
AudioDeviceInfo.convertDeviceTypeToInternalDevice(mType))
@@ -191,25 +191,25 @@ public final class AudioDeviceAddress implements Parcelable {
dest.writeString(mAddress);
}
- private AudioDeviceAddress(@NonNull Parcel in) {
+ private AudioDevice(@NonNull Parcel in) {
mRole = in.readInt();
mType = in.readInt();
mAddress = in.readString();
}
- public static final @NonNull Parcelable.Creator<AudioDeviceAddress> CREATOR =
- new Parcelable.Creator<AudioDeviceAddress>() {
+ public static final @NonNull Parcelable.Creator<AudioDevice> CREATOR =
+ new Parcelable.Creator<AudioDevice>() {
/**
- * Rebuilds an AudioDeviceAddress previously stored with writeToParcel().
- * @param p Parcel object to read the AudioDeviceAddress from
- * @return a new AudioDeviceAddress created from the data in the parcel
+ * Rebuilds an AudioDevice previously stored with writeToParcel().
+ * @param p Parcel object to read the AudioDevice from
+ * @return a new AudioDevice created from the data in the parcel
*/
- public AudioDeviceAddress createFromParcel(Parcel p) {
- return new AudioDeviceAddress(p);
+ public AudioDevice createFromParcel(Parcel p) {
+ return new AudioDevice(p);
}
- public AudioDeviceAddress[] newArray(int size) {
- return new AudioDeviceAddress[size];
+ public AudioDevice[] newArray(int size) {
+ return new AudioDevice[size];
}
};
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 7b17f9f764fb..4a1088bfa877 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1596,7 +1596,7 @@ public class AudioManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean setPreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy,
- @NonNull AudioDeviceAddress device) {
+ @NonNull AudioDevice device) {
Objects.requireNonNull(strategy);
Objects.requireNonNull(device);
try {
@@ -1611,7 +1611,7 @@ public class AudioManager {
/**
* @hide
* Removes the preferred audio device previously set with
- * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAddress)}.
+ * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDevice)}.
* @param strategy the audio strategy whose routing will be affected
* @return true if the operation was successful, false otherwise (invalid strategy, or no
* device set for example)
@@ -1632,14 +1632,14 @@ public class AudioManager {
/**
* @hide
* Return the preferred device for an audio strategy, previously set with
- * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAddress)}
+ * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDevice)}
* @param strategy the strategy to query
* @return the preferred device for that strategy, or null if none was ever set or if the
* strategy is invalid
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public @Nullable AudioDeviceAddress getPreferredDeviceForStrategy(
+ public @Nullable AudioDevice getPreferredDeviceForStrategy(
@NonNull AudioProductStrategy strategy) {
Objects.requireNonNull(strategy);
try {
@@ -4379,7 +4379,7 @@ public class AudioManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public @NonNull List<AudioDeviceAddress> getDevicesForAttributes(
+ public @NonNull List<AudioDevice> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
Objects.requireNonNull(attributes);
final IAudioService service = getService();
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 02cb8aafea0c..0a0f7f643ac9 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1085,18 +1085,18 @@ public class AudioSystem
* @return an empty list if there was an issue with the request, a list of audio devices
* otherwise (typically one device, except for duplicated paths).
*/
- public static @NonNull ArrayList<AudioDeviceAddress> getDevicesForAttributes(
+ public static @NonNull ArrayList<AudioDevice> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
Objects.requireNonNull(attributes);
- final AudioDeviceAddress[] devices = new AudioDeviceAddress[MAX_DEVICE_ROUTING];
+ final AudioDevice[] devices = new AudioDevice[MAX_DEVICE_ROUTING];
final int res = getDevicesForAttributes(attributes, devices);
- final ArrayList<AudioDeviceAddress> routeDevices = new ArrayList<>();
+ final ArrayList<AudioDevice> routeDevices = new ArrayList<>();
if (res != SUCCESS) {
Log.e(TAG, "error " + res + " in getDevicesForAttributes for " + attributes);
return routeDevices;
}
- for (AudioDeviceAddress device : devices) {
+ for (AudioDevice device : devices) {
if (device != null) {
routeDevices.add(device);
}
@@ -1106,12 +1106,12 @@ public class AudioSystem
/**
* Maximum number of audio devices a track is ever routed to, determines the size of the
- * array passed to {@link #getDevicesForAttributes(AudioAttributes, AudioDeviceAddress[])}
+ * array passed to {@link #getDevicesForAttributes(AudioAttributes, AudioDevice[])}
*/
private static final int MAX_DEVICE_ROUTING = 4;
private static native int getDevicesForAttributes(@NonNull AudioAttributes aa,
- @NonNull AudioDeviceAddress[] devices);
+ @NonNull AudioDevice[] devices);
/** @hide returns true if master mono is enabled. */
public static native boolean getMasterMono();
@@ -1246,7 +1246,7 @@ public class AudioSystem
* @return {@link #SUCCESS} if successfully set
*/
public static int setPreferredDeviceForStrategy(
- int strategy, @NonNull AudioDeviceAddress device) {
+ int strategy, @NonNull AudioDevice device) {
return setPreferredDeviceForStrategy(strategy,
AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
device.getAddress());
@@ -1277,7 +1277,7 @@ public class AudioSystem
* and written to the array
*/
public static native int getPreferredDeviceForStrategy(int strategy,
- AudioDeviceAddress[] device);
+ AudioDevice[] device);
// Items shared with audio service
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 64c5c05b5621..0fbc0d2180ba 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -18,7 +18,7 @@ package android.media;
import android.bluetooth.BluetoothDevice;
import android.media.AudioAttributes;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
import android.media.AudioFocusInfo;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
@@ -274,13 +274,13 @@ interface IAudioService {
boolean isCallScreeningModeSupported();
- int setPreferredDeviceForStrategy(in int strategy, in AudioDeviceAddress device);
+ int setPreferredDeviceForStrategy(in int strategy, in AudioDevice device);
int removePreferredDeviceForStrategy(in int strategy);
- AudioDeviceAddress getPreferredDeviceForStrategy(in int strategy);
+ AudioDevice getPreferredDeviceForStrategy(in int strategy);
- List<AudioDeviceAddress> getDevicesForAttributes(in AudioAttributes attributes);
+ List<AudioDevice> getDevicesForAttributes(in AudioAttributes attributes);
int setAllowedCapturePolicy(in int capturePolicy);
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index d7b74df0a5e6..64186106d2c8 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -471,7 +471,8 @@ public class MediaRouter2 {
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.isSystemRoute()
+ || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
addedRoutes.add(route);
}
}
@@ -487,7 +488,8 @@ public class MediaRouter2 {
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
mRoutes.remove(route.getId());
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.isSystemRoute()
+ || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
removedRoutes.add(route);
}
}
@@ -503,7 +505,8 @@ public class MediaRouter2 {
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.isSystemRoute()
+ || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
changedRoutes.add(route);
}
}
@@ -643,8 +646,8 @@ public class MediaRouter2 {
private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
RouteDiscoveryPreference discoveryRequest) {
return routes.stream()
- .filter(
- route -> route.hasAnyFeatures(discoveryRequest.getPreferredFeatures()))
+ .filter(route -> route.isSystemRoute()
+ || route.hasAnyFeatures(discoveryRequest.getPreferredFeatures()))
.collect(Collectors.toList());
}
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index c25a5333017b..6157ef4657ed 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -25,7 +25,7 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
import android.media.AudioDeviceInfo;
import android.media.AudioSystem;
import android.os.Build;
@@ -476,12 +476,12 @@ public class AudioEffect {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
- public AudioEffect(@NonNull UUID uuid, @NonNull AudioDeviceAddress device) {
+ public AudioEffect(@NonNull UUID uuid, @NonNull AudioDevice device) {
this(EFFECT_TYPE_NULL, Objects.requireNonNull(uuid), 0, -2, Objects.requireNonNull(device));
}
private AudioEffect(UUID type, UUID uuid, int priority,
- int audioSession, @Nullable AudioDeviceAddress device)
+ int audioSession, @Nullable AudioDevice device)
throws IllegalArgumentException, UnsupportedOperationException,
RuntimeException {
int[] id = new int[1];
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index 118f65c214e1..08953392ca18 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -113,7 +113,7 @@ public final class SoundTriggerDetector {
* This capability may or may not be supported by the system, and support can be queried
* by calling {@link SoundTriggerManager#getModuleProperties()} and checking
* {@link ModuleProperties#audioCapabilities}. The corresponding capabilities field for
- * this flag is {@link SoundTrigger.ModuleProperties#CAPABILITY_ECHO_CANCELLATION}.
+ * this flag is {@link SoundTrigger.ModuleProperties#AUDIO_CAPABILITY_ECHO_CANCELLATION}.
* If this flag is passed without the audio capability supported, there will be no audio effect
* applied.
*/
@@ -125,8 +125,9 @@ public final class SoundTriggerDetector {
* This capability may or may not be supported by the system, and support can be queried
* by calling {@link SoundTriggerManager#getModuleProperties()} and checking
* {@link ModuleProperties#audioCapabilities}. The corresponding capabilities field for
- * this flag is {@link SoundTrigger.ModuleProperties#CAPABILITY_NOISE_SUPPRESSION}. If this flag
- * is passed without the audio capability supported, there will be no audio effect applied.
+ * this flag is {@link SoundTrigger.ModuleProperties#AUDIO_CAPABILITY_NOISE_SUPPRESSION}.
+ * If this flag is passed without the audio capability supported, there will be no audio effect
+ * applied.
*/
public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 0x8;
@@ -296,10 +297,10 @@ public final class SoundTriggerDetector {
int audioCapabilities = 0;
if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION) != 0) {
- audioCapabilities |= SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION;
+ audioCapabilities |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION;
}
if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION) != 0) {
- audioCapabilities |= SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION;
+ audioCapabilities |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
}
int status;
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index dd4dac223a86..6a8483c58276 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -173,8 +173,13 @@ public final class SoundTriggerManager {
}
/**
- * Factory constructor to create a SoundModel instance for use with methods in this
- * class.
+ * Factory constructor to a voice model to be used with {@link SoundTriggerManager}
+ *
+ * @param modelUuid Unique identifier associated with the model.
+ * @param vendorUuid Unique identifier associated the calling vendor.
+ * @param data Model's data.
+ * @param version Version identifier for the model.
+ * @return Voice model
*/
@NonNull
public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid,
@@ -186,8 +191,12 @@ public final class SoundTriggerManager {
}
/**
- * Factory constructor to create a SoundModel instance for use with methods in this
- * class.
+ * Factory constructor to a voice model to be used with {@link SoundTriggerManager}
+ *
+ * @param modelUuid Unique identifier associated with the model.
+ * @param vendorUuid Unique identifier associated the calling vendor.
+ * @param data Model's data.
+ * @return Voice model
*/
@NonNull
public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid,
@@ -195,20 +204,40 @@ public final class SoundTriggerManager {
return create(modelUuid, vendorUuid, data, -1);
}
+ /**
+ * Get the model's unique identifier
+ *
+ * @return UUID associated with the model
+ */
@NonNull
public UUID getModelUuid() {
return mGenericSoundModel.uuid;
}
+ /**
+ * Get the model's vendor identifier
+ *
+ * @return UUID associated with the vendor of the model
+ */
@NonNull
public UUID getVendorUuid() {
return mGenericSoundModel.vendorUuid;
}
+ /**
+ * Get the model's version
+ *
+ * @return Version associated with the model
+ */
public int getVersion() {
return mGenericSoundModel.version;
}
+ /**
+ * Get the underlying model data
+ *
+ * @return Backing data of the model
+ */
@Nullable
public byte[] getModelData() {
return mGenericSoundModel.data;
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 5e012447e9dd..3561f8393eea 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -74,6 +74,8 @@ public class Tuner implements AutoCloseable {
private List<Integer> mFrontendIds;
private Frontend mFrontend;
private EventHandler mHandler;
+ @Nullable
+ private FrontendInfo mFrontendInfo;
private List<Integer> mLnbIds;
private Lnb mLnb;
@@ -97,6 +99,7 @@ public class Tuner implements AutoCloseable {
public Tuner(@NonNull Context context, @NonNull String tvInputSessionId,
@TvInputService.PriorityHintUseCaseType int useCase,
@Nullable OnResourceLostListener listener) {
+ nativeSetup();
mContext = context;
}
@@ -185,7 +188,7 @@ public class Tuner implements AutoCloseable {
/**
* Listener for resource lost.
*
- * <p>Resource is reclaimed and tuner instance is forced to close.
+ * <p>Insufficient resources are reclaimed by higher priority clients.
*/
public interface OnResourceLostListener {
/**
@@ -292,6 +295,7 @@ public class Tuner implements AutoCloseable {
@Result
public int tune(@NonNull FrontendSettings settings) {
TunerUtils.checkTunerPermission(mContext);
+ mFrontendInfo = null;
return nativeTune(settings.getType(), settings);
}
@@ -333,6 +337,7 @@ public class Tuner implements AutoCloseable {
}
mScanCallback = scanCallback;
mScanCallbackExecutor = executor;
+ mFrontendInfo = null;
return nativeScan(settings.getType(), settings, scanType);
}
@@ -468,7 +473,10 @@ public class Tuner implements AutoCloseable {
if (mFrontend == null) {
throw new IllegalStateException("frontend is not initialized");
}
- return nativeGetFrontendInfo(mFrontend.mId);
+ if (mFrontendInfo == null) {
+ mFrontendInfo = nativeGetFrontendInfo(mFrontend.mId);
+ }
+ return mFrontendInfo;
}
/**
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 9b37f955fa17..71ba59c8a707 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -724,18 +724,21 @@ status_t JMediaCodec::getOutputFrame(
}
if (buffer->size() > 0) {
- // asC2Buffer clears internal reference, so set the reference again.
std::shared_ptr<C2Buffer> c2Buffer = buffer->asC2Buffer();
- buffer->copy(c2Buffer);
if (c2Buffer) {
+ // asC2Buffer clears internal reference, so set the reference again.
+ buffer->copy(c2Buffer);
switch (c2Buffer->data().type()) {
case C2BufferData::LINEAR: {
std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
context->mBuffer = c2Buffer;
ScopedLocalRef<jobject> linearBlock{env, env->NewObject(
gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)};
- env->SetLongField(
- linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release());
+ env->CallVoidMethod(
+ linearBlock.get(),
+ gLinearBlockInfo.setInternalStateId,
+ (jlong)context.release(),
+ true);
env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get());
break;
}
@@ -744,8 +747,11 @@ status_t JMediaCodec::getOutputFrame(
context->mBuffer = c2Buffer;
ScopedLocalRef<jobject> graphicBlock{env, env->NewObject(
gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)};
- env->SetLongField(
- graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release());
+ env->CallVoidMethod(
+ graphicBlock.get(),
+ gGraphicBlockInfo.setInternalStateId,
+ (jlong)context.release(),
+ true);
env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get());
break;
}
@@ -761,16 +767,22 @@ status_t JMediaCodec::getOutputFrame(
context->mLegacyBuffer = buffer;
ScopedLocalRef<jobject> linearBlock{env, env->NewObject(
gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)};
- env->SetLongField(
- linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release());
+ env->CallVoidMethod(
+ linearBlock.get(),
+ gLinearBlockInfo.setInternalStateId,
+ (jlong)context.release(),
+ true);
env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get());
} else {
std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock};
context->mLegacyBuffer = buffer;
ScopedLocalRef<jobject> graphicBlock{env, env->NewObject(
gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)};
- env->SetLongField(
- graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release());
+ env->CallVoidMethod(
+ graphicBlock.get(),
+ gGraphicBlockInfo.setInternalStateId,
+ (jlong)context.release(),
+ true);
env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get());
}
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 81a85c9f08d6..08c3f988e85e 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -322,6 +322,179 @@ jobject JTuner::openFrontendById(int id) {
(jint) jId);
}
+jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
+
+ jint typeCap = caps.analogCaps().typeCap;
+ jint sifStandardCap = caps.analogCaps().sifStandardCap;
+ return env->NewObject(clazz, capsInit, typeCap, sifStandardCap);
+}
+
+jobject JTuner::getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIII)V");
+
+ jint bandwidthCap = caps.atsc3Caps().bandwidthCap;
+ jint modulationCap = caps.atsc3Caps().modulationCap;
+ jint timeInterleaveModeCap = caps.atsc3Caps().timeInterleaveModeCap;
+ jint codeRateCap = caps.atsc3Caps().codeRateCap;
+ jint fecCap = caps.atsc3Caps().fecCap;
+ jint demodOutputFormatCap = caps.atsc3Caps().demodOutputFormatCap;
+
+ return env->NewObject(clazz, capsInit, bandwidthCap, modulationCap, timeInterleaveModeCap,
+ codeRateCap, fecCap, demodOutputFormatCap);
+}
+
+jobject JTuner::getAtscFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AtscFrontendCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(I)V");
+
+ jint modulationCap = caps.atscCaps().modulationCap;
+
+ return env->NewObject(clazz, capsInit, modulationCap);
+}
+
+jobject JTuner::getDvbcFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(III)V");
+
+ jint modulationCap = caps.dvbcCaps().modulationCap;
+ jint fecCap = caps.dvbcCaps().fecCap;
+ jint annexCap = caps.dvbcCaps().annexCap;
+
+ return env->NewObject(clazz, capsInit, modulationCap, fecCap, annexCap);
+}
+
+jobject JTuner::getDvbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IJI)V");
+
+ jint modulationCap = caps.dvbsCaps().modulationCap;
+ jlong innerfecCap = caps.dvbsCaps().innerfecCap;
+ jint standard = caps.dvbsCaps().standard;
+
+ return env->NewObject(clazz, capsInit, modulationCap, innerfecCap, standard);
+}
+
+jobject JTuner::getDvbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIZZ)V");
+
+ jint transmissionModeCap = caps.dvbtCaps().transmissionModeCap;
+ jint bandwidthCap = caps.dvbtCaps().bandwidthCap;
+ jint constellationCap = caps.dvbtCaps().constellationCap;
+ jint coderateCap = caps.dvbtCaps().coderateCap;
+ jint hierarchyCap = caps.dvbtCaps().hierarchyCap;
+ jint guardIntervalCap = caps.dvbtCaps().guardIntervalCap;
+ jboolean isT2Supported = caps.dvbtCaps().isT2Supported;
+ jboolean isMisoSupported = caps.dvbtCaps().isMisoSupported;
+
+ return env->NewObject(clazz, capsInit, transmissionModeCap, bandwidthCap, constellationCap,
+ coderateCap, hierarchyCap, guardIntervalCap, isT2Supported, isMisoSupported);
+}
+
+jobject JTuner::getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
+
+ jint modulationCap = caps.isdbs3Caps().modulationCap;
+ jint coderateCap = caps.isdbs3Caps().coderateCap;
+
+ return env->NewObject(clazz, capsInit, modulationCap, coderateCap);
+}
+
+jobject JTuner::getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbsFrontendCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
+
+ jint modulationCap = caps.isdbsCaps().modulationCap;
+ jint coderateCap = caps.isdbsCaps().coderateCap;
+
+ return env->NewObject(clazz, capsInit, modulationCap, coderateCap);
+}
+
+jobject JTuner::getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbtFrontendCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIII)V");
+
+ jint modeCap = caps.isdbtCaps().modeCap;
+ jint bandwidthCap = caps.isdbtCaps().bandwidthCap;
+ jint modulationCap = caps.isdbtCaps().modulationCap;
+ jint coderateCap = caps.isdbtCaps().coderateCap;
+ jint guardIntervalCap = caps.isdbtCaps().guardIntervalCap;
+
+ return env->NewObject(clazz, capsInit, modeCap, bandwidthCap, modulationCap, coderateCap,
+ guardIntervalCap);
+}
+
+jobject JTuner::getFrontendInfo(int id) {
+ FrontendInfo feInfo;
+ Result res;
+ mTuner->getFrontendInfo(id, [&](Result r, const FrontendInfo& info) {
+ feInfo = info;
+ res = r;
+ });
+ if (res != Result::SUCCESS) {
+ return NULL;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendInfo");
+ jmethodID infoInit = env->GetMethodID(clazz, "<init>",
+ "(IIIIIIII[ILandroid/media/tv/tuner/frontend/FrontendCapabilities;)V");
+
+ jint type = (jint) feInfo.type;
+ jint minFrequency = feInfo.minFrequency;
+ jint maxFrequency = feInfo.maxFrequency;
+ jint minSymbolRate = feInfo.minSymbolRate;
+ jint maxSymbolRate = feInfo.maxSymbolRate;
+ jint acquireRange = feInfo.acquireRange;
+ jint exclusiveGroupId = feInfo.exclusiveGroupId;
+ jintArray statusCaps = env->NewIntArray(feInfo.statusCaps.size());
+ env->SetIntArrayRegion(
+ statusCaps, 0, feInfo.statusCaps.size(),
+ reinterpret_cast<jint*>(&feInfo.statusCaps[0]));
+ FrontendInfo::FrontendCapabilities caps = feInfo.frontendCaps;
+
+ jobject jcaps = NULL;
+ switch(feInfo.type) {
+ case FrontendType::ANALOG:
+ jcaps = getAnalogFrontendCaps(env, caps);
+ break;
+ case FrontendType::ATSC3:
+ jcaps = getAtsc3FrontendCaps(env, caps);
+ break;
+ case FrontendType::ATSC:
+ jcaps = getAtscFrontendCaps(env, caps);
+ break;
+ case FrontendType::DVBC:
+ jcaps = getDvbcFrontendCaps(env, caps);
+ break;
+ case FrontendType::DVBS:
+ jcaps = getDvbsFrontendCaps(env, caps);
+ break;
+ case FrontendType::DVBT:
+ jcaps = getDvbtFrontendCaps(env, caps);
+ break;
+ case FrontendType::ISDBS:
+ jcaps = getIsdbsFrontendCaps(env, caps);
+ break;
+ case FrontendType::ISDBS3:
+ jcaps = getIsdbs3FrontendCaps(env, caps);
+ break;
+ case FrontendType::ISDBT:
+ jcaps = getIsdbtFrontendCaps(env, caps);
+ break;
+ default:
+ break;
+ }
+
+ return env->NewObject(
+ clazz, infoInit, (jint) id, type, minFrequency, maxFrequency, minSymbolRate,
+ maxSymbolRate, acquireRange, exclusiveGroupId, statusCaps, jcaps);
+}
+
jobject JTuner::getLnbIds() {
ALOGD("JTuner::getLnbIds()");
mTuner->getLnbIds([&](Result, const hidl_vec<FrontendId>& lnbIds) {
@@ -1162,8 +1335,9 @@ static int android_media_tv_Tuner_disconnect_cicam(JNIEnv*, jobject) {
return 0;
}
-static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv*, jobject, jint) {
- return NULL;
+static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thiz, jint id) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->getFrontendInfo(id);
}
static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 978d9c6e1059..cfe99b399979 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -39,6 +39,7 @@ using ::android::hardware::tv::tuner::V1_0::DemuxPid;
using ::android::hardware::tv::tuner::V1_0::DvrType;
using ::android::hardware::tv::tuner::V1_0::FrontendEventType;
using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::FrontendInfo;
using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
using ::android::hardware::tv::tuner::V1_0::FrontendScanType;
@@ -132,6 +133,7 @@ struct JTuner : public RefBase {
sp<ITuner> getTunerService();
jobject getFrontendIds();
jobject openFrontendById(int id);
+ jobject getFrontendInfo(int id);
int tune(const FrontendSettings& settings);
int stopTune();
int scan(const FrontendSettings& settings, FrontendScanType scanType);
@@ -158,6 +160,15 @@ private:
sp<ILnb> mLnb;
sp<IDemux> mDemux;
int mDemuxId;
+ static jobject getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+ static jobject getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+ static jobject getAtscFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+ static jobject getDvbcFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+ static jobject getDvbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+ static jobject getDvbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+ static jobject getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+ static jobject getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+ static jobject getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
};
} // namespace android
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index c1143ce9c3dc..2e4d2140809e 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -213,12 +213,12 @@ int32_t AImageDecoderHeaderInfo_getDataSpace(const AImageDecoderHeaderInfo* info
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
- // Note: This recomputes the data space because it's possible the client has
- // changed the output color space, so we cannot rely on it. Alternatively,
+ // Note: This recomputes the color type because it's possible the client has
+ // changed the output color type, so we cannot rely on it. Alternatively,
// we could store the ADataSpace in the ImageDecoder.
const ImageDecoder* imageDecoder = toDecoder(info);
SkColorType colorType = imageDecoder->mCodec->computeOutputColorType(kN32_SkColorType);
- sk_sp<SkColorSpace> colorSpace = imageDecoder->mCodec->computeOutputColorSpace(colorType);
+ sk_sp<SkColorSpace> colorSpace = imageDecoder->getDefaultColorSpace();
return uirenderer::ColorSpaceToADataSpace(colorSpace.get(), colorType);
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 38cf5ab9afe6..617305cf6e5e 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -38,7 +38,6 @@ import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Path;
import android.provider.DocumentsContract.Root;
-import android.provider.MediaStore;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
@@ -100,7 +99,6 @@ public class ExternalStorageProvider extends FileSystemProvider {
private static final String ROOT_ID_PRIMARY_EMULATED =
DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID;
- private static final String ROOT_ID_HOME = "home";
private static final String GET_DOCUMENT_URI_CALL = "get_document_uri";
private static final String GET_MEDIA_URI_CALL = "get_media_uri";
@@ -156,7 +154,6 @@ public class ExternalStorageProvider extends FileSystemProvider {
private void updateVolumesLocked() {
mRoots.clear();
- VolumeInfo primaryVolume = null;
final int userId = UserHandle.myUserId();
final List<VolumeInfo> volumes = mStorageManager.getVolumes();
for (VolumeInfo volume : volumes) {
@@ -234,8 +231,6 @@ public class ExternalStorageProvider extends FileSystemProvider {
}
if (volume.isPrimary()) {
- // save off the primary volume for subsequent "Home" dir initialization.
- primaryVolume = volume;
root.flags |= Root.FLAG_ADVANCED;
}
// Dunno when this would NOT be the case, but never hurts to be correct.
@@ -259,37 +254,6 @@ public class ExternalStorageProvider extends FileSystemProvider {
}
}
- // Finally, if primary storage is available we add the "Documents" directory.
- // If I recall correctly the actual directory is created on demand
- // by calling either getPathForUser, or getInternalPathForUser.
- if (primaryVolume != null && primaryVolume.isVisible()) {
- final RootInfo root = new RootInfo();
- root.rootId = ROOT_ID_HOME;
- mRoots.put(root.rootId, root);
- root.title = getContext().getString(R.string.root_documents);
-
- // Only report bytes on *volumes*...as a matter of policy.
- root.reportAvailableBytes = false;
- root.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_SEARCH
- | Root.FLAG_SUPPORTS_IS_CHILD;
-
- // Dunno when this would NOT be the case, but never hurts to be correct.
- if (primaryVolume.isMountedWritable()) {
- root.flags |= Root.FLAG_SUPPORTS_CREATE;
- }
-
- // Create the "Documents" directory on disk (don't use the localized title).
- root.visiblePath = new File(
- primaryVolume.getPathForUser(userId), Environment.DIRECTORY_DOCUMENTS);
- root.path = new File(
- primaryVolume.getInternalPathForUser(userId), Environment.DIRECTORY_DOCUMENTS);
- try {
- root.docId = getDocIdForFile(root.path);
- } catch (FileNotFoundException e) {
- throw new IllegalStateException(e);
- }
- }
-
Log.d(TAG, "After updating volumes, found " + mRoots.size() + " active roots");
// Note this affects content://com.android.externalstorage.documents/root/39BD-07C5
diff --git a/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml
new file mode 100644
index 000000000000..04de17474368
--- /dev/null
+++ b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"
+ android:fillColor="#4285F4"/>
+</vector>
diff --git a/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_grey.xml b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_grey.xml
new file mode 100644
index 000000000000..b4145f23f61a
--- /dev/null
+++ b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_grey.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"
+ android:fillColor="#757575"/>
+</vector>
diff --git a/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
new file mode 100644
index 000000000000..e6f8c01e22ac
--- /dev/null
+++ b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
@@ -0,0 +1,92 @@
+<?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.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/entity_header"
+ style="@style/EntityHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/entity_header_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/entity_header_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/entity_header_icon_personal"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:scaleType="fitCenter"
+ android:antialias="true"/>
+
+ <TextView
+ android:id="@+id/install_type"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:text="Personal"/>
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/entity_header_swap_horiz"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:scaleType="fitCenter"
+ android:antialias="true"
+ android:src="@drawable/ic_swap_horiz_grey"/>
+
+ <LinearLayout
+ android:id="@+id/entity_header_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/entity_header_icon_work"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:scaleType="fitCenter"
+ android:antialias="true"/>
+ <TextView
+ android:id="@+id/install_type"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:text="Work"/>
+ </LinearLayout>
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
new file mode 100644
index 000000000000..332d6c7bc0fa
--- /dev/null
+++ b/packages/SettingsLib/res/values/config.xml
@@ -0,0 +1,24 @@
+<?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.
+ -->
+<!-- These resources are around just to allow their values to be customized -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Threshold in micro watts below which a charger is rated as "slow"; 1A @ 5V -->
+ <integer name="config_chargingSlowlyThreshold">5000000</integer>
+
+ <!-- Threshold in micro watts above which a charger is rated as "fast"; 1.5A @ 5V -->
+ <integer name="config_chargingFastThreshold">7500000</integer>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index dd21e5c07b13..4d96251043ce 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -970,7 +970,7 @@
<!-- Title for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
<string name="accessibility_display_daltonizer_preference_title">Color correction</string>
<!-- Subtitle for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
- <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps people with color blindness to see more accurate colors</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps people with colorblindness see more accurate colors</string>
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
@@ -1034,8 +1034,10 @@
<string name="battery_info_status_unknown">Unknown</string>
<!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging from an unknown source. -->
<string name="battery_info_status_charging">Charging</string>
- <!-- [CHAR_LIMIT=20] Battery use screen with lower case. Battery status shown in chart label when charging from an unknown source. -->
- <string name="battery_info_status_charging_lower">charging</string>
+ <!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging speed is fast. -->
+ <string name="battery_info_status_charging_fast">Charging rapidly</string>
+ <!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging speed is slow. -->
+ <string name="battery_info_status_charging_slow">Charging slowly</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_discharging">Not charging</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 213e3657254d..f4857932064f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -35,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.UserIcons;
import com.android.launcher3.icons.IconFactory;
import com.android.settingslib.drawable.UserIconDrawable;
+import com.android.settingslib.fuelgauge.BatteryStatus;
import java.text.NumberFormat;
@@ -122,7 +123,7 @@ public class Utils {
public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
final int iconSize = UserIconDrawable.getSizeForList(context);
if (user.isManagedProfile()) {
- Drawable drawable = UserIconDrawable.getManagedUserDrawable(context);
+ Drawable drawable = UserIconDrawable.getManagedUserDrawable(context);
drawable.setBounds(0, 0, iconSize, iconSize);
return drawable;
}
@@ -164,20 +165,43 @@ public class Utils {
return (level * 100) / scale;
}
- public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) {
- int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS,
+ /**
+ * Get battery status string
+ *
+ * @param context the context
+ * @param batteryChangedIntent battery broadcast intent received from {@link
+ * Intent.ACTION_BATTERY_CHANGED}.
+ * @return battery status string
+ */
+ public static String getBatteryStatus(Context context, Intent batteryChangedIntent) {
+ final int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
- String statusString;
- if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
- statusString = res.getString(R.string.battery_info_status_charging);
- } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
- statusString = res.getString(R.string.battery_info_status_discharging);
- } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
- statusString = res.getString(R.string.battery_info_status_not_charging);
- } else if (status == BatteryManager.BATTERY_STATUS_FULL) {
+ final Resources res = context.getResources();
+
+ String statusString = res.getString(R.string.battery_info_status_unknown);
+ final BatteryStatus batteryStatus = new BatteryStatus(batteryChangedIntent);
+
+ if (batteryStatus.isCharged()) {
statusString = res.getString(R.string.battery_info_status_full);
} else {
- statusString = res.getString(R.string.battery_info_status_unknown);
+ if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+ switch (batteryStatus.getChargingSpeed(context)) {
+ case BatteryStatus.CHARGING_FAST:
+ statusString = res.getString(R.string.battery_info_status_charging_fast);
+ break;
+ case BatteryStatus.CHARGING_SLOWLY:
+ statusString = res.getString(R.string.battery_info_status_charging_slow);
+ break;
+ default:
+ statusString = res.getString(R.string.battery_info_status_charging);
+ break;
+ }
+
+ } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
+ statusString = res.getString(R.string.battery_info_status_discharging);
+ } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
+ statusString = res.getString(R.string.battery_info_status_not_charging);
+ }
}
return statusString;
@@ -211,7 +235,7 @@ public class Utils {
/**
* This method computes disabled color from normal color
*
- * @param context
+ * @param context the context
* @param inputColor normal color.
* @return disabled color.
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
new file mode 100644
index 000000000000..bc40903d88e4
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
@@ -0,0 +1,147 @@
+/*
+ * 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.settingslib.fuelgauge;
+
+import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
+import static android.os.BatteryManager.BATTERY_STATUS_FULL;
+import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
+import static android.os.BatteryManager.EXTRA_HEALTH;
+import static android.os.BatteryManager.EXTRA_LEVEL;
+import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
+import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
+import static android.os.BatteryManager.EXTRA_PLUGGED;
+import static android.os.BatteryManager.EXTRA_STATUS;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import com.android.settingslib.R;
+
+/**
+ * Stores and computes some battery information.
+ */
+public class BatteryStatus {
+ private static final int LOW_BATTERY_THRESHOLD = 20;
+ private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
+
+ public static final int CHARGING_UNKNOWN = -1;
+ public static final int CHARGING_SLOWLY = 0;
+ public static final int CHARGING_REGULAR = 1;
+ public static final int CHARGING_FAST = 2;
+
+ public final int status;
+ public final int level;
+ public final int plugged;
+ public final int health;
+ public final int maxChargingWattage;
+
+ public BatteryStatus(int status, int level, int plugged, int health,
+ int maxChargingWattage) {
+ this.status = status;
+ this.level = level;
+ this.plugged = plugged;
+ this.health = health;
+ this.maxChargingWattage = maxChargingWattage;
+ }
+
+ public BatteryStatus(Intent batteryChangedIntent) {
+ status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
+ plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0);
+ level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0);
+ health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
+
+ final int maxChargingMicroAmp = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT,
+ -1);
+ int maxChargingMicroVolt = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1);
+
+ if (maxChargingMicroVolt <= 0) {
+ maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT;
+ }
+ if (maxChargingMicroAmp > 0) {
+ // Calculating muW = muA * muV / (10^6 mu^2 / mu); splitting up the divisor
+ // to maintain precision equally on both factors.
+ maxChargingWattage = (maxChargingMicroAmp / 1000)
+ * (maxChargingMicroVolt / 1000);
+ } else {
+ maxChargingWattage = -1;
+ }
+ }
+
+ /**
+ * Determine whether the device is plugged in (USB, power, or wireless).
+ *
+ * @return true if the device is plugged in.
+ */
+ public boolean isPluggedIn() {
+ return plugged == BatteryManager.BATTERY_PLUGGED_AC
+ || plugged == BatteryManager.BATTERY_PLUGGED_USB
+ || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
+ }
+
+ /**
+ * Determine whether the device is plugged in (USB, power).
+ *
+ * @return true if the device is plugged in wired (as opposed to wireless)
+ */
+ public boolean isPluggedInWired() {
+ return plugged == BatteryManager.BATTERY_PLUGGED_AC
+ || plugged == BatteryManager.BATTERY_PLUGGED_USB;
+ }
+
+ /**
+ * Whether or not the device is charged. Note that some devices never return 100% for
+ * battery level, so this allows either battery level or status to determine if the
+ * battery is charged.
+ *
+ * @return true if the device is charged
+ */
+ public boolean isCharged() {
+ return status == BATTERY_STATUS_FULL || level >= 100;
+ }
+
+ /**
+ * Whether battery is low and needs to be charged.
+ *
+ * @return true if battery is low
+ */
+ public boolean isBatteryLow() {
+ return level < LOW_BATTERY_THRESHOLD;
+ }
+
+ /**
+ * Return current chargin speed is fast, slow or normal.
+ *
+ * @return the charing speed
+ */
+ public final int getChargingSpeed(Context context) {
+ final int slowThreshold = context.getResources().getInteger(
+ R.integer.config_chargingSlowlyThreshold);
+ final int fastThreshold = context.getResources().getInteger(
+ R.integer.config_chargingFastThreshold);
+ return maxChargingWattage <= 0 ? CHARGING_UNKNOWN :
+ maxChargingWattage < slowThreshold ? CHARGING_SLOWLY :
+ maxChargingWattage > fastThreshold ? CHARGING_FAST :
+ CHARGING_REGULAR;
+ }
+
+ @Override
+ public String toString() {
+ return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged
+ + ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}";
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 3a53d29f7618..e551b69e024a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -19,7 +19,8 @@ import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.util.Log;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
import android.util.Pair;
import com.android.settingslib.R;
@@ -35,8 +36,9 @@ public class BluetoothMediaDevice extends MediaDevice {
private CachedBluetoothDevice mCachedDevice;
- BluetoothMediaDevice(Context context, CachedBluetoothDevice device) {
- super(context, MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
+ BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
+ MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName) {
+ super(context, MediaDeviceType.TYPE_BLUETOOTH_DEVICE, routerManager, info, packageName);
mCachedDevice = device;
initDeviceRecord();
}
@@ -65,20 +67,6 @@ public class BluetoothMediaDevice extends MediaDevice {
return MediaDeviceUtils.getId(mCachedDevice);
}
- @Override
- public boolean connect() {
- //TODO(b/117129183): add callback to notify LocalMediaManager connection state.
- final boolean isConnected = mCachedDevice.setActive();
- setConnectedRecord();
- Log.d(TAG, "connect() device : " + getName() + ", is selected : " + isConnected);
- return isConnected;
- }
-
- @Override
- public void disconnect() {
- //TODO(b/117129183): disconnected last select device
- }
-
/**
* Get current CachedBluetoothDevice
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
index 3a807c9b9b77..d84788b2739f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
@@ -157,7 +157,7 @@ public class BluetoothMediaManager extends MediaManager implements BluetoothCall
private void addMediaDevice(CachedBluetoothDevice cachedDevice) {
MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(cachedDevice));
if (mediaDevice == null) {
- mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice);
+ mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice, null, null, null);
cachedDevice.registerCallback(mDeviceAttributeChangeCallback);
mLastAddedDevice = mediaDevice;
mMediaDevices.add(mediaDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index d287f95e504a..b9081f241a91 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -16,9 +16,12 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.text.TextUtils;
+import android.util.Log;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -30,16 +33,9 @@ public class InfoMediaDevice extends MediaDevice {
private static final String TAG = "InfoMediaDevice";
- private final MediaRoute2Info mRouteInfo;
- private final MediaRouter2Manager mRouterManager;
- private final String mPackageName;
-
InfoMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
String packageName) {
- super(context, MediaDeviceType.TYPE_CAST_DEVICE);
- mRouterManager = routerManager;
- mRouteInfo = info;
- mPackageName = packageName;
+ super(context, MediaDeviceType.TYPE_CAST_DEVICE, routerManager, info, packageName);
initDeviceRecord();
}
@@ -67,13 +63,6 @@ public class InfoMediaDevice extends MediaDevice {
}
@Override
- public boolean connect() {
- setConnectedRecord();
- mRouterManager.selectRoute(mPackageName, mRouteInfo);
- return true;
- }
-
- @Override
public void requestSetVolume(int volume) {
mRouterManager.requestSetVolume(mRouteInfo, volume);
}
@@ -89,11 +78,30 @@ public class InfoMediaDevice extends MediaDevice {
}
@Override
- public void disconnect() {
- //TODO(b/144535188): disconnected last select device
+ public String getClientPackageName() {
+ return mRouteInfo.getClientPackageName();
}
@Override
+ public String getClientAppLabel() {
+ final String packageName = mRouteInfo.getClientPackageName();
+ if (TextUtils.isEmpty(packageName)) {
+ Log.d(TAG, "Client package name is empty");
+ return mContext.getResources().getString(R.string.unknown);
+ }
+ try {
+ final PackageManager packageManager = mContext.getPackageManager();
+ final String appLabel = packageManager.getApplicationLabel(
+ packageManager.getApplicationInfo(packageName, 0)).toString();
+ if (!TextUtils.isEmpty(appLabel)) {
+ return appLabel;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "unable to find " + packageName);
+ }
+ return mContext.getResources().getString(R.string.unknown);
+ }
+
public boolean isConnected() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index a4be46cf1f63..e91096760180 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -15,13 +15,23 @@
*/
package com.android.settingslib.media;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_BLUETOOTH;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_UNKNOWN;
+
import android.app.Notification;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RoutingSessionInfo;
import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
import java.util.List;
import java.util.concurrent.Executor;
@@ -44,11 +54,14 @@ public class InfoMediaManager extends MediaManager {
String mPackageName;
private MediaDevice mCurrentConnectedDevice;
+ private LocalBluetoothManager mBluetoothManager;
- public InfoMediaManager(Context context, String packageName, Notification notification) {
+ public InfoMediaManager(Context context, String packageName, Notification notification,
+ LocalBluetoothManager localBluetoothManager) {
super(context, notification);
mRouterManager = MediaRouter2Manager.getInstance(context);
+ mBluetoothManager = localBluetoothManager;
if (!TextUtils.isEmpty(packageName)) {
mPackageName = packageName;
}
@@ -94,23 +107,73 @@ public class InfoMediaManager extends MediaManager {
private void buildAllRoutes() {
for (MediaRoute2Info route : mRouterManager.getAllRoutes()) {
- final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
- mPackageName);
- mMediaDevices.add(device);
+ addMediaDevice(route);
}
}
private void buildAvailableRoutes() {
for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) {
- final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
- mPackageName);
- if (TextUtils.equals(route.getClientPackageName(), mPackageName)) {
- mCurrentConnectedDevice = device;
- }
- mMediaDevices.add(device);
+ addMediaDevice(route);
}
}
+ private void addMediaDevice(MediaRoute2Info route) {
+ final int deviceType = route.getDeviceType();
+ MediaDevice mediaDevice = null;
+ switch (deviceType) {
+ case DEVICE_TYPE_UNKNOWN:
+ //TODO(b/148765806): use correct device type once api is ready.
+ final String defaultRoute = "DEFAULT_ROUTE";
+ if (TextUtils.equals(defaultRoute, route.getOriginalId())) {
+ mediaDevice =
+ new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName);
+ } else {
+ mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
+ mPackageName);
+ if (!TextUtils.isEmpty(mPackageName)
+ && TextUtils.equals(route.getClientPackageName(), mPackageName)) {
+ mCurrentConnectedDevice = mediaDevice;
+ }
+ }
+ break;
+ case DEVICE_TYPE_REMOTE_TV:
+ break;
+ case DEVICE_TYPE_BLUETOOTH:
+ final BluetoothDevice device =
+ BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getOriginalId());
+ final CachedBluetoothDevice cachedDevice =
+ mBluetoothManager.getCachedDeviceManager().findDevice(device);
+ mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice, mRouterManager,
+ route, mPackageName);
+ break;
+ default:
+ Log.w(TAG, "addMediaDevice() unknown device type : " + deviceType);
+ break;
+
+ }
+
+ if (mediaDevice != null) {
+ mMediaDevices.add(mediaDevice);
+ }
+ }
+
+ /**
+ * Transfer MediaDevice for media without package name.
+ */
+ public boolean connectDeviceWithoutPackageName(MediaDevice device) {
+ boolean isConnected = false;
+ final List<RoutingSessionInfo> infos = mRouterManager.getActiveSessions();
+ if (infos.size() > 0) {
+ final RoutingSessionInfo info = infos.get(0);
+ final MediaRouter2Manager.RoutingController controller =
+ mRouterManager.getControllerForSession(info);
+
+ controller.transferToRoute(device.mRouteInfo);
+ isConnected = true;
+ }
+ return isConnected;
+ }
+
class RouterManagerCallback extends MediaRouter2Manager.Callback {
@Override
@@ -124,5 +187,10 @@ public class InfoMediaManager extends MediaManager {
refreshDevices();
}
}
+
+ @Override
+ public void onRoutesChanged(List<MediaRoute2Info> routes) {
+ refreshDevices();
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 50196d2b2994..a1342ecdcfa7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -18,6 +18,7 @@ package com.android.settingslib.media;
import android.app.Notification;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.IntDef;
@@ -57,7 +58,6 @@ public class LocalMediaManager implements BluetoothCallback {
final MediaDeviceCallback mMediaDeviceCallback = new MediaDeviceCallback();
private Context mContext;
- private BluetoothMediaManager mBluetoothMediaManager;
private LocalBluetoothManager mLocalBluetoothManager;
private InfoMediaManager mInfoMediaManager;
private String mPackageName;
@@ -97,18 +97,18 @@ public class LocalMediaManager implements BluetoothCallback {
return;
}
- mBluetoothMediaManager =
- new BluetoothMediaManager(context, mLocalBluetoothManager, notification);
- mInfoMediaManager = new InfoMediaManager(context, packageName, notification);
+ mInfoMediaManager =
+ new InfoMediaManager(context, packageName, notification, mLocalBluetoothManager);
}
@VisibleForTesting
LocalMediaManager(Context context, LocalBluetoothManager localBluetoothManager,
- BluetoothMediaManager bluetoothMediaManager, InfoMediaManager infoMediaManager) {
+ InfoMediaManager infoMediaManager, String packageName) {
mContext = context;
mLocalBluetoothManager = localBluetoothManager;
- mBluetoothMediaManager = bluetoothMediaManager;
mInfoMediaManager = infoMediaManager;
+ mPackageName = packageName;
+
}
/**
@@ -135,7 +135,12 @@ public class LocalMediaManager implements BluetoothCallback {
mCurrentConnectedDevice.disconnect();
}
- final boolean isConnected = device.connect();
+ boolean isConnected = false;
+ if (TextUtils.isEmpty(mPackageName)) {
+ isConnected = mInfoMediaManager.connectDeviceWithoutPackageName(device);
+ } else {
+ isConnected = device.connect();
+ }
if (isConnected) {
mCurrentConnectedDevice = device;
}
@@ -159,29 +164,10 @@ public class LocalMediaManager implements BluetoothCallback {
*/
public void startScan() {
mMediaDevices.clear();
- mBluetoothMediaManager.registerCallback(mMediaDeviceCallback);
- mBluetoothMediaManager.startScan();
mInfoMediaManager.registerCallback(mMediaDeviceCallback);
mInfoMediaManager.startScan();
}
- private void addPhoneDeviceIfNecessary() {
- // add phone device to list if there have any Bluetooth device and cast device.
- if (mMediaDevices.size() > 0 && !mMediaDevices.contains(mPhoneDevice)) {
- if (mPhoneDevice == null) {
- mPhoneDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
- }
- mMediaDevices.add(mPhoneDevice);
- }
- }
-
- private void removePhoneMediaDeviceIfNecessary() {
- // if PhoneMediaDevice is the last item in the list, remove it.
- if (mMediaDevices.size() == 1 && mMediaDevices.contains(mPhoneDevice)) {
- mMediaDevices.clear();
- }
- }
-
void dispatchDeviceListUpdate() {
synchronized (mCallbacks) {
Collections.sort(mMediaDevices, COMPARATOR);
@@ -203,8 +189,6 @@ public class LocalMediaManager implements BluetoothCallback {
* Stop scan MediaDevice
*/
public void stopScan() {
- mBluetoothMediaManager.unregisterCallback(mMediaDeviceCallback);
- mBluetoothMediaManager.stopScan();
mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
mInfoMediaManager.stopScan();
}
@@ -227,6 +211,22 @@ public class LocalMediaManager implements BluetoothCallback {
}
/**
+ * Find the MediaDevice from all media devices by id.
+ *
+ * @param id the unique id of MediaDevice
+ * @return MediaDevice
+ */
+ public MediaDevice getMediaDeviceById(String id) {
+ for (MediaDevice mediaDevice : mMediaDevices) {
+ if (mediaDevice.getId().equals(id)) {
+ return mediaDevice;
+ }
+ }
+ Log.i(TAG, "Unable to find device " + id);
+ return null;
+ }
+
+ /**
* Find the current connected MediaDevice.
*
* @return MediaDevice
@@ -235,15 +235,34 @@ public class LocalMediaManager implements BluetoothCallback {
return mCurrentConnectedDevice;
}
+ /**
+ * Find the active MediaDevice.
+ *
+ * @param type the media device type.
+ * @return MediaDevice list
+ */
+ public List<MediaDevice> getActiveMediaDevice(@MediaDevice.MediaDeviceType int type) {
+ final List<MediaDevice> devices = new ArrayList<>();
+ for (MediaDevice device : mMediaDevices) {
+ if (type == device.mType && device.getClientPackageName() != null) {
+ devices.add(device);
+ }
+ }
+ return devices;
+ }
+
private MediaDevice updateCurrentConnectedDevice() {
+ MediaDevice phoneMediaDevice = null;
for (MediaDevice device : mMediaDevices) {
if (device instanceof BluetoothMediaDevice) {
if (isConnected(((BluetoothMediaDevice) device).getCachedDevice())) {
return device;
}
+ } else if (device instanceof PhoneMediaDevice) {
+ phoneMediaDevice = device;
}
}
- return mMediaDevices.contains(mPhoneDevice) ? mPhoneDevice : null;
+ return mMediaDevices.contains(phoneMediaDevice) ? phoneMediaDevice : null;
}
private boolean isConnected(CachedBluetoothDevice device) {
@@ -256,38 +275,24 @@ public class LocalMediaManager implements BluetoothCallback {
public void onDeviceAdded(MediaDevice device) {
if (!mMediaDevices.contains(device)) {
mMediaDevices.add(device);
- addPhoneDeviceIfNecessary();
dispatchDeviceListUpdate();
}
}
@Override
public void onDeviceListAdded(List<MediaDevice> devices) {
- for (MediaDevice device : devices) {
- if (getMediaDeviceById(mMediaDevices, device.getId()) == null) {
- mMediaDevices.add(device);
- }
- }
- addPhoneDeviceIfNecessary();
+ mMediaDevices.clear();
+ mMediaDevices.addAll(devices);
final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
mCurrentConnectedDevice = infoMediaDevice != null
? infoMediaDevice : updateCurrentConnectedDevice();
- updatePhoneMediaDeviceSummary();
dispatchDeviceListUpdate();
}
- private void updatePhoneMediaDeviceSummary() {
- if (mPhoneDevice != null) {
- ((PhoneMediaDevice) mPhoneDevice)
- .updateSummary(mCurrentConnectedDevice == mPhoneDevice);
- }
- }
-
@Override
public void onDeviceRemoved(MediaDevice device) {
if (mMediaDevices.contains(device)) {
mMediaDevices.remove(device);
- removePhoneMediaDeviceIfNecessary();
dispatchDeviceListUpdate();
}
}
@@ -295,7 +300,6 @@ public class LocalMediaManager implements BluetoothCallback {
@Override
public void onDeviceListRemoved(List<MediaDevice> devices) {
mMediaDevices.removeAll(devices);
- removePhoneMediaDeviceIfNecessary();
dispatchDeviceListUpdate();
}
@@ -308,7 +312,6 @@ public class LocalMediaManager implements BluetoothCallback {
return;
}
mCurrentConnectedDevice = connectDevice;
- updatePhoneMediaDeviceSummary();
dispatchDeviceAttributesChanged();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 839d528406cd..580e086973d6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -17,9 +17,12 @@ package com.android.settingslib.media;
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
import android.text.TextUtils;
import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -40,14 +43,23 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
int TYPE_BLUETOOTH_DEVICE = 3;
}
+ @VisibleForTesting
+ int mType;
+
private int mConnectedRecord;
- protected Context mContext;
- protected int mType;
+ protected final Context mContext;
+ protected final MediaRoute2Info mRouteInfo;
+ protected final MediaRouter2Manager mRouterManager;
+ protected final String mPackageName;
- MediaDevice(Context context, @MediaDeviceType int type) {
+ MediaDevice(Context context, @MediaDeviceType int type, MediaRouter2Manager routerManager,
+ MediaRoute2Info info, String packageName) {
mType = type;
mContext = context;
+ mRouteInfo = info;
+ mRouterManager = routerManager;
+ mPackageName = packageName;
}
void initDeviceRecord() {
@@ -83,13 +95,6 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
*/
public abstract String getId();
- /**
- * Transfer MediaDevice for media
- *
- * @return result of transfer media
- */
- public abstract boolean connect();
-
void setConnectedRecord() {
mConnectedRecord++;
ConnectionRecordManager.getInstance().setConnectionRecord(mContext, getId(),
@@ -97,11 +102,6 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
}
/**
- * Stop transfer MediaDevice
- */
- public abstract void disconnect();
-
- /**
* According the MediaDevice type to check whether we are connected to this MediaDevice.
*
* @return Whether it is connected.
@@ -162,6 +162,23 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
}
/**
+ * Transfer MediaDevice for media
+ *
+ * @return result of transfer media
+ */
+ public boolean connect() {
+ setConnectedRecord();
+ mRouterManager.selectRoute(mPackageName, mRouteInfo);
+ return true;
+ }
+
+ /**
+ * Stop transfer MediaDevice
+ */
+ public void disconnect() {
+ }
+
+ /**
* Rules:
* 1. If there is one of the connected devices identified as a carkit, this carkit will
* be always on the top of the device list. Rule 2 and Rule 3 can’t overrule this rule.
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
index 248b118f6ece..f341bf17edd3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
@@ -32,6 +32,16 @@ public class MediaOutputSliceConstants {
public static final String KEY_REMOTE_MEDIA = "remote_media";
/**
+ * Key for the {@link android.media.session.MediaSession.Token}.
+ */
+ public static final String KEY_MEDIA_SESSION_TOKEN = "key_media_session_token";
+
+ /**
+ * Key for the {@link android.media.RoutingSessionInfo#getId()}
+ */
+ public static final String KEY_SESSION_INFO_ID = "key_session_info_id";
+
+ /**
* Activity Action: Show a settings dialog containing {@link MediaDevice} to transfer media.
*/
public static final String ACTION_MEDIA_OUTPUT =
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index af91c3464194..166fbaa2a337 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -17,14 +17,11 @@ package com.android.settingslib.media;
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.util.Log;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
import com.android.settingslib.R;
-import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.BluetoothUtils;
-import com.android.settingslib.bluetooth.HearingAidProfile;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
/**
* PhoneMediaDevice extends MediaDevice to represents Phone device.
@@ -35,15 +32,12 @@ public class PhoneMediaDevice extends MediaDevice {
public static final String ID = "phone_media_device_id_1";
- private LocalBluetoothProfileManager mProfileManager;
- private LocalBluetoothManager mLocalBluetoothManager;
private String mSummary = "";
- PhoneMediaDevice(Context context, LocalBluetoothManager localBluetoothManager) {
- super(context, MediaDeviceType.TYPE_PHONE_DEVICE);
+ PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
+ String packageName) {
+ super(context, MediaDeviceType.TYPE_PHONE_DEVICE, routerManager, info, packageName);
- mLocalBluetoothManager = localBluetoothManager;
- mProfileManager = mLocalBluetoothManager.getProfileManager();
initDeviceRecord();
}
@@ -69,32 +63,6 @@ public class PhoneMediaDevice extends MediaDevice {
}
@Override
- public boolean connect() {
- final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
- final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
-
- // Some device may not have HearingAidProfile, consider all situation to set active device.
- boolean isConnected = false;
- if (hapProfile != null && a2dpProfile != null) {
- isConnected = hapProfile.setActiveDevice(null) && a2dpProfile.setActiveDevice(null);
- } else if (a2dpProfile != null) {
- isConnected = a2dpProfile.setActiveDevice(null);
- } else if (hapProfile != null) {
- isConnected = hapProfile.setActiveDevice(null);
- }
- updateSummary(isConnected);
- setConnectedRecord();
-
- Log.d(TAG, "connect() device : " + getName() + ", is selected : " + isConnected);
- return isConnected;
- }
-
- @Override
- public void disconnect() {
- updateSummary(false);
- }
-
- @Override
public boolean isConnected() {
return true;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 11829451f640..6307caf5e02b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -31,6 +31,7 @@ import android.content.Intent;
import android.content.res.Resources;
import android.location.LocationManager;
import android.media.AudioManager;
+import android.os.BatteryManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
@@ -122,12 +123,12 @@ public class UtilsTest {
public void testGetDefaultStorageManagerDaysToRetain_storageManagerDaysToRetainUsesResources() {
Resources resources = mock(Resources.class);
when(resources.getInteger(
- eq(
- com.android
- .internal
- .R
- .integer
- .config_storageManagerDaystoRetainDefault)))
+ eq(
+ com.android
+ .internal
+ .R
+ .integer
+ .config_storageManagerDaystoRetainDefault)))
.thenReturn(60);
assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60);
}
@@ -147,7 +148,8 @@ public class UtilsTest {
private static Map<String, Integer> map = new HashMap<>();
@Implementation
- public static boolean putIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+ public static boolean putIntForUser(ContentResolver cr, String name, int value,
+ int userHandle) {
map.put(name, value);
return true;
}
@@ -312,4 +314,33 @@ public class UtilsTest {
assertThat(Utils.getCombinedServiceState(mServiceState)).isEqualTo(
ServiceState.STATE_OUT_OF_SERVICE);
}
+
+ @Test
+ public void getBatteryStatus_statusIsFull_returnFullString() {
+ final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_LEVEL, 100);
+ final Resources resources = mContext.getResources();
+
+ assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+ resources.getString(R.string.battery_info_status_full));
+ }
+
+ @Test
+ public void getBatteryStatus_batteryLevelIs100_returnFullString() {
+ final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_STATUS,
+ BatteryManager.BATTERY_STATUS_FULL);
+ final Resources resources = mContext.getResources();
+
+ assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+ resources.getString(R.string.battery_info_status_full));
+ }
+
+ @Test
+ public void getBatteryStatus_batteryLevel99_returnChargingString() {
+ final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_STATUS,
+ BatteryManager.BATTERY_STATUS_CHARGING);
+ final Resources resources = mContext.getResources();
+
+ assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+ resources.getString(R.string.battery_info_status_charging));
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
index e0e2fd6860ff..a39bcb7f08f6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
@@ -51,21 +51,7 @@ public class BluetoothMediaDeviceTest {
when(mDevice.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
when(mDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
- mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice);
- }
-
- @Test
- public void connect_setActiveSuccess_isConnectedReturnTrue() {
- when(mDevice.setActive()).thenReturn(true);
-
- assertThat(mBluetoothMediaDevice.connect()).isTrue();
- }
-
- @Test
- public void connect_setActiveFail_isConnectedReturnFalse() {
- when(mDevice.setActive()).thenReturn(false);
-
- assertThat(mBluetoothMediaDevice.connect()).isFalse();
+ mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null, null);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index c9db0d13a7e7..04ceb2147c6e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -18,10 +18,12 @@ package com.android.settingslib.media;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageStats;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
@@ -34,11 +36,14 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowPackageManager;
@RunWith(RobolectricTestRunner.class)
public class InfoMediaDeviceTest {
private static final String TEST_PACKAGE_NAME = "com.test.packagename";
+ private static final String TEST_PACKAGE_NAME2 = "com.test.packagename2";
private static final String TEST_ID = "test_id";
private static final String TEST_NAME = "test_name";
@@ -50,11 +55,24 @@ public class InfoMediaDeviceTest {
private Context mContext;
private InfoMediaDevice mInfoMediaDevice;
+ private ShadowPackageManager mShadowPackageManager;
+ private ApplicationInfo mAppInfo;
+ private PackageInfo mPackageInfo;
+ private PackageStats mPackageStats;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
+ mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
+ mAppInfo = new ApplicationInfo();
+ mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
+ mAppInfo.packageName = TEST_PACKAGE_NAME;
+ mAppInfo.name = TEST_NAME;
+ mPackageInfo = new PackageInfo();
+ mPackageInfo.packageName = TEST_PACKAGE_NAME;
+ mPackageInfo.applicationInfo = mAppInfo;
+ mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
mInfoMediaDevice = new InfoMediaDevice(mContext, mRouterManager, mRouteInfo,
TEST_PACKAGE_NAME);
@@ -90,9 +108,30 @@ public class InfoMediaDeviceTest {
}
@Test
- public void connect_shouldSelectRoute() {
- mInfoMediaDevice.connect();
+ public void getClientPackageName_returnPackageName() {
+ when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ assertThat(mInfoMediaDevice.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+ }
+
+ @Test
+ public void getClientAppLabel_matchedPackageName_returnLabel() {
+ when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(
+ mContext.getResources().getString(R.string.unknown));
+
+ mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
+
+ assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(TEST_NAME);
+ }
+
+ @Test
+ public void getClientAppLabel_noMatchedPackageName_returnDefault() {
+ mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
+ when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME2);
- verify(mRouterManager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo);
+ assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(
+ mContext.getResources().getString(R.string.unknown));
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 3726fb24479a..05f5fa0afc9e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -25,6 +25,9 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RoutingSessionInfo;
+
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
import org.junit.Before;
import org.junit.Test;
@@ -45,6 +48,8 @@ public class InfoMediaManagerTest {
@Mock
private MediaRouter2Manager mRouterManager;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
private InfoMediaManager mInfoMediaManager;
private Context mContext;
@@ -54,7 +59,8 @@ public class InfoMediaManagerTest {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- mInfoMediaManager = new InfoMediaManager(mContext, TEST_PACKAGE_NAME, null);
+ mInfoMediaManager =
+ new InfoMediaManager(mContext, TEST_PACKAGE_NAME, null, mLocalBluetoothManager);
mInfoMediaManager.mRouterManager = mRouterManager;
}
@@ -142,4 +148,59 @@ public class InfoMediaManagerTest {
assertThat(mInfoMediaManager.mMediaDevices).hasSize(0);
}
+
+ @Test
+ public void onRoutesChanged_getAvailableRoutes_shouldAddMediaDevice() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ when(info.getId()).thenReturn(TEST_ID);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ routes.add(info);
+ when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn(routes);
+
+ final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+ assertThat(mediaDevice).isNull();
+
+ mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes);
+
+ final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+ assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+ assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+ }
+
+ @Test
+ public void onRoutesChanged_buildAllRoutes_shouldAddMediaDevice() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ when(info.getId()).thenReturn(TEST_ID);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ routes.add(info);
+ when(mRouterManager.getAllRoutes()).thenReturn(routes);
+
+ final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+ assertThat(mediaDevice).isNull();
+
+ mInfoMediaManager.mPackageName = "";
+ mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes);
+
+ final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+ assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+ }
+
+ @Test
+ public void connectDeviceWithoutPackageName_noSession_returnFalse() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, info,
+ TEST_PACKAGE_NAME);
+
+ final List<RoutingSessionInfo> infos = new ArrayList<>();
+
+ when(mRouterManager.getActiveSessions()).thenReturn(infos);
+
+ assertThat(mInfoMediaManager.connectDeviceWithoutPackageName(device)).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index c780a64c2fb4..3d67ba053a45 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -80,7 +80,7 @@ public class LocalMediaManagerTest {
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
- mBluetoothMediaManager, mInfoMediaManager);
+ mInfoMediaManager, "com.test.packagename");
}
@Test
@@ -163,14 +163,14 @@ public class LocalMediaManagerTest {
}
@Test
- public void onDeviceAdded_mediaDeviceAndPhoneDeviceNotExistInList_addBothDevice() {
+ public void onDeviceAdded_addDevice() {
final MediaDevice device = mock(MediaDevice.class);
assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceAdded(device);
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
verify(mCallback).onDeviceListUpdate(any());
}
@@ -206,7 +206,7 @@ public class LocalMediaManagerTest {
}
@Test
- public void onDeviceListAdded_phoneDeviceNotExistInList_addPhoneDeviceAndDevicesList() {
+ public void onDeviceListAdded_addDevicesList() {
final List<MediaDevice> devices = new ArrayList<>();
final MediaDevice device1 = mock(MediaDevice.class);
final MediaDevice device2 = mock(MediaDevice.class);
@@ -220,12 +220,12 @@ public class LocalMediaManagerTest {
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
verify(mCallback).onDeviceListUpdate(any());
}
@Test
- public void onDeviceListAdded_phoneDeviceExistInList_addDeviceList() {
+ public void onDeviceListAdded_addDeviceList() {
final List<MediaDevice> devices = new ArrayList<>();
final MediaDevice device1 = mock(MediaDevice.class);
final MediaDevice device2 = mock(MediaDevice.class);
@@ -245,12 +245,12 @@ public class LocalMediaManagerTest {
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(4);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
verify(mCallback).onDeviceListUpdate(any());
}
@Test
- public void onDeviceRemoved_phoneDeviceIsLastDeviceAfterRemoveMediaDevice_removeBothDevice() {
+ public void onDeviceRemoved_removeDevice() {
final MediaDevice device1 = mock(MediaDevice.class);
mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
mLocalMediaManager.mMediaDevices.add(device1);
@@ -260,7 +260,7 @@ public class LocalMediaManagerTest {
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceRemoved(device1);
- assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
verify(mCallback).onDeviceListUpdate(any());
}
@@ -298,7 +298,7 @@ public class LocalMediaManagerTest {
}
@Test
- public void onDeviceListRemoved_phoneDeviceIsLastDeviceAfterRemoveDeviceList_removeAll() {
+ public void onDeviceListRemoved_removeAll() {
final List<MediaDevice> devices = new ArrayList<>();
final MediaDevice device1 = mock(MediaDevice.class);
final MediaDevice device2 = mock(MediaDevice.class);
@@ -311,7 +311,8 @@ public class LocalMediaManagerTest {
assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
mLocalMediaManager.registerCallback(mCallback);
- mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devices);
+ mLocalMediaManager.mMediaDeviceCallback
+ .onDeviceListRemoved(mLocalMediaManager.mMediaDevices);
assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
verify(mCallback).onDeviceListUpdate(any());
@@ -384,4 +385,38 @@ public class LocalMediaManagerTest {
verify(mCallback).onDeviceAttributesChanged();
}
+
+ @Test
+ public void getActiveMediaDevice_checkList() {
+ final List<MediaDevice> devices = new ArrayList<>();
+ final MediaDevice device1 = mock(MediaDevice.class);
+ final MediaDevice device2 = mock(MediaDevice.class);
+ final MediaDevice device3 = mock(MediaDevice.class);
+ device1.mType = MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE;
+ device2.mType = MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE;
+ device3.mType = MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE;
+ when(device1.getClientPackageName()).thenReturn(TEST_DEVICE_ID_1);
+ when(device2.getClientPackageName()).thenReturn(TEST_DEVICE_ID_2);
+ when(device3.getClientPackageName()).thenReturn(TEST_DEVICE_ID_3);
+ when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
+ when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
+ when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
+ devices.add(device1);
+ devices.add(device2);
+ devices.add(device3);
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
+
+ List<MediaDevice> activeDevices = mLocalMediaManager.getActiveMediaDevice(
+ MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
+ assertThat(activeDevices).containsExactly(device1);
+
+ activeDevices = mLocalMediaManager.getActiveMediaDevice(
+ MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE);
+ assertThat(activeDevices).containsExactly(device2);
+
+ activeDevices = mLocalMediaManager.getActiveMediaDevice(
+ MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
+ assertThat(activeDevices).containsExactly(device3);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 02cb83e0281a..fb8b78b22055 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -17,6 +17,7 @@ package com.android.settingslib.media;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
@@ -83,6 +84,14 @@ public class MediaDeviceTest {
@Mock
private MediaRoute2Info mRouteInfo3;
@Mock
+ private MediaRoute2Info mBluetoothRouteInfo1;
+ @Mock
+ private MediaRoute2Info mBluetoothRouteInfo2;
+ @Mock
+ private MediaRoute2Info mBluetoothRouteInfo3;
+ @Mock
+ private MediaRoute2Info mPhoneRouteInfo;
+ @Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private HearingAidProfile mHapProfile;
@@ -90,6 +99,8 @@ public class MediaDeviceTest {
private A2dpProfile mA2dpProfile;
@Mock
private BluetoothDevice mDevice;
+ @Mock
+ private MediaRouter2Manager mMediaRouter2Manager;
private BluetoothMediaDevice mBluetoothMediaDevice1;
private BluetoothMediaDevice mBluetoothMediaDevice2;
@@ -100,7 +111,6 @@ public class MediaDeviceTest {
private InfoMediaDevice mInfoMediaDevice3;
private List<MediaDevice> mMediaDevices = new ArrayList<>();
private PhoneMediaDevice mPhoneMediaDevice;
- private MediaRouter2Manager mMediaRouter2Manager;
@Before
public void setUp() {
@@ -133,17 +143,24 @@ public class MediaDeviceTest {
when(mProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
when(mA2dpProfile.getActiveDevice()).thenReturn(mDevice);
- mBluetoothMediaDevice1 = new BluetoothMediaDevice(mContext, mCachedDevice1);
- mBluetoothMediaDevice2 = new BluetoothMediaDevice(mContext, mCachedDevice2);
- mBluetoothMediaDevice3 = new BluetoothMediaDevice(mContext, mCachedDevice3);
- mMediaRouter2Manager = MediaRouter2Manager.getInstance(mContext);
+ mBluetoothMediaDevice1 =
+ new BluetoothMediaDevice(mContext, mCachedDevice1, mMediaRouter2Manager,
+ mBluetoothRouteInfo1, TEST_PACKAGE_NAME);
+ mBluetoothMediaDevice2 =
+ new BluetoothMediaDevice(mContext, mCachedDevice2, mMediaRouter2Manager,
+ mBluetoothRouteInfo2, TEST_PACKAGE_NAME);
+ mBluetoothMediaDevice3 =
+ new BluetoothMediaDevice(mContext, mCachedDevice3, mMediaRouter2Manager,
+ mBluetoothRouteInfo3, TEST_PACKAGE_NAME);
mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
TEST_PACKAGE_NAME);
mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2,
TEST_PACKAGE_NAME);
mInfoMediaDevice3 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo3,
TEST_PACKAGE_NAME);
- mPhoneMediaDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
+ mPhoneMediaDevice =
+ new PhoneMediaDevice(mContext, mMediaRouter2Manager, mPhoneRouteInfo,
+ TEST_PACKAGE_NAME);
}
@Test
@@ -370,4 +387,11 @@ public class MediaDeviceTest {
assertThat(mMediaDevices.get(5)).isEqualTo(mBluetoothMediaDevice1);
assertThat(mMediaDevices.get(6)).isEqualTo(mBluetoothMediaDevice2);
}
+
+ @Test
+ public void connect_shouldSelectRoute() {
+ mInfoMediaDevice1.connect();
+
+ verify(mMediaRouter2Manager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo1);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
index 0752dc03f397..db984fb8dc26 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
@@ -18,21 +18,13 @@ package com.android.settingslib.media;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.when;
-
-import android.bluetooth.BluetoothDevice;
import android.content.Context;
import com.android.settingslib.R;
-import com.android.settingslib.bluetooth.A2dpProfile;
-import com.android.settingslib.bluetooth.HearingAidProfile;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@@ -40,17 +32,6 @@ import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class PhoneMediaDeviceTest {
- @Mock
- private LocalBluetoothProfileManager mLocalProfileManager;
- @Mock
- private LocalBluetoothManager mLocalBluetoothManager;
- @Mock
- private HearingAidProfile mHapProfile;
- @Mock
- private A2dpProfile mA2dpProfile;
- @Mock
- private BluetoothDevice mDevice;
-
private Context mContext;
private PhoneMediaDevice mPhoneMediaDevice;
@@ -59,68 +40,8 @@ public class PhoneMediaDeviceTest {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
- when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
- when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
- when(mA2dpProfile.getActiveDevice()).thenReturn(mDevice);
-
- mPhoneMediaDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
- }
-
- @Test
- public void connect_phoneDeviceSetActiveSuccess_isConnectedReturnTrue() {
- when(mA2dpProfile.setActiveDevice(null)).thenReturn(true);
- when(mHapProfile.setActiveDevice(null)).thenReturn(true);
-
- assertThat(mPhoneMediaDevice.connect()).isTrue();
- }
-
- @Test
- public void connect_a2dpProfileSetActiveFail_isConnectedReturnFalse() {
- when(mA2dpProfile.setActiveDevice(null)).thenReturn(false);
- when(mHapProfile.setActiveDevice(null)).thenReturn(true);
-
- assertThat(mPhoneMediaDevice.connect()).isFalse();
- }
-
- @Test
- public void connect_hearingAidProfileSetActiveFail_isConnectedReturnFalse() {
- when(mA2dpProfile.setActiveDevice(null)).thenReturn(true);
- when(mHapProfile.setActiveDevice(null)).thenReturn(false);
-
- assertThat(mPhoneMediaDevice.connect()).isFalse();
- }
-
- @Test
- public void connect_hearingAidAndA2dpProfileSetActiveFail_isConnectedReturnFalse() {
- when(mA2dpProfile.setActiveDevice(null)).thenReturn(false);
- when(mHapProfile.setActiveDevice(null)).thenReturn(false);
-
- assertThat(mPhoneMediaDevice.connect()).isFalse();
- }
-
- @Test
- public void connect_hearingAidProfileIsNullAndA2dpProfileNotNull_isConnectedReturnTrue() {
- when(mLocalProfileManager.getHearingAidProfile()).thenReturn(null);
-
- when(mA2dpProfile.setActiveDevice(null)).thenReturn(true);
- assertThat(mPhoneMediaDevice.connect()).isTrue();
- }
-
- @Test
- public void connect_hearingAidProfileNotNullAndA2dpProfileIsNull_isConnectedReturnTrue() {
- when(mLocalProfileManager.getA2dpProfile()).thenReturn(null);
-
- when(mHapProfile.setActiveDevice(null)).thenReturn(true);
- assertThat(mPhoneMediaDevice.connect()).isTrue();
- }
-
- @Test
- public void connect_hearingAidProfileAndA2dpProfileIsNull_isConnectedReturnFalse() {
- when(mLocalProfileManager.getA2dpProfile()).thenReturn(null);
- when(mLocalProfileManager.getHearingAidProfile()).thenReturn(null);
-
- assertThat(mPhoneMediaDevice.connect()).isFalse();
+ mPhoneMediaDevice =
+ new PhoneMediaDevice(mContext, null, null, null);
}
@Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1d679c7bcbdd..5946f2158ac8 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -308,6 +308,22 @@
android:excludeFromRecents="true"
android:exported="false" />
+ <!--
+ The following is used as a no-op/null home activity when
+ no other MAIN/HOME activity is present (e.g., in CSI).
+ -->
+ <activity android:name=".NullHome"
+ android:excludeFromRecents="true"
+ android:label=""
+ android:screenOrientation="nosensor">
+ <!-- The priority here is set to be lower than that for Settings -->
+ <intent-filter android:priority="-1100">
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.HOME" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<receiver
android:name=".BugreportRequestedReceiver"
android:permission="android.permission.TRIGGER_SHELL_BUGREPORT">
diff --git a/packages/Shell/res/layout/null_home_finishing_boot.xml b/packages/Shell/res/layout/null_home_finishing_boot.xml
new file mode 100644
index 000000000000..5f9563a5d25c
--- /dev/null
+++ b/packages/Shell/res/layout/null_home_finishing_boot.xml
@@ -0,0 +1,44 @@
+<?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
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#80000000"
+ android:forceHasOverlappingRendering="false">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="center"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="40sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:text="@*android:string/android_start_title"/>
+ <ProgressBar
+ style="@android:style/Widget.Material.ProgressBar.Horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12.75dp"
+ android:colorControlActivated="?android:attr/textColorPrimary"
+ android:indeterminate="true"/>
+ </LinearLayout>
+</FrameLayout>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 48d405a4b91f..18145939c384 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -97,6 +97,7 @@ import java.io.ByteArrayInputStream;
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.InputStream;
@@ -193,9 +194,7 @@ public class BugreportProgressService extends Service {
* <p>
* Must be a path supported by its FileProvider.
*/
- // TODO: use the same variable for both dir
- private static final String SCREENSHOT_DIR = "bugreports";
- private static final String BUGREPORT_DIR = "/bugreports";
+ private static final String BUGREPORT_DIR = "bugreports";
private static final String NOTIFICATION_CHANNEL_ID = "bugreports";
@@ -230,7 +229,7 @@ public class BugreportProgressService extends Service {
private final BugreportInfoDialog mInfoDialog = new BugreportInfoDialog();
- private File mScreenshotsDir;
+ private File mBugreportsDir;
private BugreportManager mBugreportManager;
@@ -263,11 +262,12 @@ public class BugreportProgressService extends Service {
mScreenshotHandler = new ScreenshotHandler("BugreportProgressServiceScreenshotThread");
startSelfIntent = new Intent(this, this.getClass());
- mScreenshotsDir = new File(getFilesDir(), SCREENSHOT_DIR);
- if (!mScreenshotsDir.exists()) {
- Log.i(TAG, "Creating directory " + mScreenshotsDir + " to store temporary screenshots");
- if (!mScreenshotsDir.mkdir()) {
- Log.w(TAG, "Could not create directory " + mScreenshotsDir);
+ mBugreportsDir = new File(getFilesDir(), BUGREPORT_DIR);
+ if (!mBugreportsDir.exists()) {
+ Log.i(TAG, "Creating directory " + mBugreportsDir
+ + " to store bugreports and screenshots");
+ if (!mBugreportsDir.mkdir()) {
+ Log.w(TAG, "Could not create directory " + mBugreportsDir);
}
}
final Configuration conf = mContext.getResources().getConfiguration();
@@ -372,7 +372,7 @@ public class BugreportProgressService extends Service {
@Override
public void onFinished() {
mInfo.renameBugreportFile();
- mInfo.renameScreenshots(mScreenshotsDir);
+ mInfo.renameScreenshots();
synchronized (mLock) {
sendBugreportFinishedBroadcastLocked();
}
@@ -406,7 +406,7 @@ public class BugreportProgressService extends Service {
mInfo.bugreportFile);
} else {
trackInfoWithIdLocked();
- cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE);
+ cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE, mBugreportsDir);
final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
intent.putExtra(EXTRA_BUGREPORT, bugreportFilePath);
intent.putExtra(EXTRA_SCREENSHOT, getScreenshotForIntent(mInfo));
@@ -418,7 +418,8 @@ public class BugreportProgressService extends Service {
private static void sendRemoteBugreportFinishedBroadcast(Context context,
String bugreportFileName, File bugreportFile) {
- cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE);
+ cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE,
+ bugreportFile.getParentFile());
final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
final Uri bugreportUri = getUri(context, bugreportFile);
final String bugreportHash = generateFileHash(bugreportFileName);
@@ -468,12 +469,12 @@ public class BugreportProgressService extends Service {
return fileHash;
}
- static void cleanupOldFiles(final int minCount, final long minAge) {
+ static void cleanupOldFiles(final int minCount, final long minAge, File bugreportsDir) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
- FileUtils.deleteOlderFiles(new File(BUGREPORT_DIR), minCount, minAge);
+ FileUtils.deleteOlderFiles(bugreportsDir, minCount, minAge);
} catch (RuntimeException e) {
Log.e(TAG, "RuntimeException deleting old files", e);
}
@@ -604,18 +605,25 @@ public class BugreportProgressService extends Service {
String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
BugreportInfo info = new BugreportInfo(mContext, baseName, name,
- shareTitle, shareDescription, bugreportType);
+ shareTitle, shareDescription, bugreportType, mBugreportsDir);
+ ParcelFileDescriptor bugreportFd;
+ ParcelFileDescriptor screenshotFd;
- ParcelFileDescriptor bugreportFd = info.createBugreportFd();
- if (bugreportFd == null) {
- Log.e(TAG, "Bugreport parcel file descriptor is null.");
- return;
- }
- ParcelFileDescriptor screenshotFd = info.createScreenshotFd();
- if (screenshotFd == null) {
- Log.e(TAG, "Screenshot parcel file descriptor is null. Deleting bugreport file");
- FileUtils.closeQuietly(bugreportFd);
- info.bugreportFile.delete();
+ try {
+ bugreportFd = info.createAndGetBugreportFd();
+ if (bugreportFd == null) {
+ Log.e(TAG, "Bugreport parcel file descriptor is null.");
+ return;
+ }
+ screenshotFd = info.createAndGetDefaultScreenshotFd();
+ if (screenshotFd == null) {
+ Log.e(TAG, "Screenshot parcel file descriptor is null. Deleting bugreport file");
+ FileUtils.closeQuietly(bugreportFd);
+ info.bugreportFile.delete();
+ return;
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error in generating bugreport files: ", e);
return;
}
mBugreportManager = (BugreportManager) mContext.getSystemService(
@@ -639,21 +647,24 @@ public class BugreportProgressService extends Service {
}
}
- private static ParcelFileDescriptor createReadWriteFile(File file) {
+ private static ParcelFileDescriptor getFd(File file) {
try {
- file.createNewFile();
- file.setReadable(true, true);
- file.setWritable(true, true);
-
- ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
+ return ParcelFileDescriptor.open(file,
ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
- return fd;
- } catch (IOException e) {
+ } catch (FileNotFoundException e) {
Log.i(TAG, "Error in generating bugreports: ", e);
}
return null;
}
+ private static void createReadWriteFile(File file) throws IOException {
+ if (!file.exists()) {
+ file.createNewFile();
+ file.setReadable(true, true);
+ file.setWritable(true, true);
+ }
+ }
+
/**
* Updates the system notification for a given bugreport.
*/
@@ -874,7 +885,7 @@ public class BugreportProgressService extends Service {
return;
}
final String screenshotPath =
- new File(mScreenshotsDir, info.getPathNextScreenshot()).getAbsolutePath();
+ new File(mBugreportsDir, info.getPathNextScreenshot()).getAbsolutePath();
Message.obtain(mScreenshotHandler, MSG_SCREENSHOT_REQUEST, id, UNUSED_ARG2, screenshotPath)
.sendToTarget();
@@ -921,7 +932,7 @@ public class BugreportProgressService extends Service {
info.addScreenshot(screenshotFile);
if (info.finished) {
Log.d(TAG, "Screenshot finished after bugreport; updating share notification");
- info.renameScreenshots(mScreenshotsDir);
+ info.renameScreenshots();
sendBugreportNotification(info, mTakingScreenshot);
}
msg = mContext.getString(R.string.bugreport_screenshot_taken);
@@ -1030,11 +1041,10 @@ public class BugreportProgressService extends Service {
/**
* Build {@link Intent} that can be used to share the given bugreport.
*/
- private static Intent buildSendIntent(Context context, BugreportInfo info,
- File screenshotsDir) {
+ private static Intent buildSendIntent(Context context, BugreportInfo info) {
// Rename files (if required) before sharing
info.renameBugreportFile();
- info.renameScreenshots(screenshotsDir);
+ info.renameScreenshots();
// Files are kept on private storage, so turn into Uris that we can
// grant temporary permissions for.
final Uri bugreportUri;
@@ -1120,7 +1130,7 @@ public class BugreportProgressService extends Service {
addDetailsToZipFile(info);
- final Intent sendIntent = buildSendIntent(mContext, info, mScreenshotsDir);
+ final Intent sendIntent = buildSendIntent(mContext, info);
if (sendIntent == null) {
Log.w(TAG, "Stopping progres on ID " + id + " because share intent could not be built");
synchronized (mLock) {
@@ -1813,24 +1823,37 @@ public class BugreportProgressService extends Service {
*/
BugreportInfo(Context context, String baseName, String name,
@Nullable String shareTitle, @Nullable String shareDescription,
- @BugreportParams.BugreportMode int type) {
+ @BugreportParams.BugreportMode int type, File bugreportsDir) {
this.context = context;
this.name = this.initialName = name;
this.shareTitle = shareTitle == null ? "" : shareTitle;
this.shareDescription = shareDescription == null ? "" : shareDescription;
this.type = type;
this.baseName = baseName;
+ createBugreportFile(bugreportsDir);
+ createScreenshotFile(bugreportsDir);
}
- ParcelFileDescriptor createBugreportFd() {
- bugreportFile = new File(BUGREPORT_DIR, getFileName(this, ".zip"));
- return createReadWriteFile(bugreportFile);
+ void createBugreportFile(File bugreportsDir) {
+ bugreportFile = new File(bugreportsDir, getFileName(this, ".zip"));
}
- ParcelFileDescriptor createScreenshotFd() {
- File screenshotFile = new File(BUGREPORT_DIR, getScreenshotName("default"));
+ void createScreenshotFile(File bugreportsDir) {
+ File screenshotFile = new File(bugreportsDir, getScreenshotName("default"));
addScreenshot(screenshotFile);
- return createReadWriteFile(screenshotFile);
+ }
+
+ ParcelFileDescriptor createAndGetBugreportFd() throws IOException {
+ createReadWriteFile(bugreportFile);
+ return getFd(bugreportFile);
+ }
+
+ ParcelFileDescriptor createAndGetDefaultScreenshotFd() throws IOException {
+ if (screenshotFiles.isEmpty()) {
+ return null;
+ }
+ createReadWriteFile(screenshotFiles.get(0));
+ return getFd(screenshotFiles.get(0));
}
/**
@@ -1859,7 +1882,7 @@ public class BugreportProgressService extends Service {
* Rename all screenshots files so that they contain the new {@code name} instead of the
* {@code initialName} if user has changed it.
*/
- void renameScreenshots(File screenshotDir) {
+ void renameScreenshots() {
if (TextUtils.isEmpty(name)) {
return;
}
@@ -1869,7 +1892,7 @@ public class BugreportProgressService extends Service {
final String newName = oldName.replaceFirst(initialName, name);
final File newFile;
if (!newName.equals(oldName)) {
- final File renamedFile = new File(screenshotDir, newName);
+ final File renamedFile = new File(oldFile.getParentFile(), newName);
Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile);
newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile;
} else {
@@ -1889,7 +1912,8 @@ public class BugreportProgressService extends Service {
* Rename bugreport file to include the name given by user via UI
*/
void renameBugreportFile() {
- File newBugreportFile = new File(BUGREPORT_DIR, getFileName(this, ".zip"));
+ File newBugreportFile = new File(bugreportFile.getParentFile(),
+ getFileName(this, ".zip"));
if (!newBugreportFile.getPath().equals(bugreportFile.getPath())) {
if (bugreportFile.renameTo(newBugreportFile)) {
bugreportFile = newBugreportFile;
diff --git a/packages/Shell/src/com/android/shell/NullHome.java b/packages/Shell/src/com/android/shell/NullHome.java
new file mode 100644
index 000000000000..bd975614a50a
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/NullHome.java
@@ -0,0 +1,43 @@
+/*
+ * 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.shell;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * This covers the fallback case where no launcher is available.
+ * Usually Settings.apk has one fallback home activity.
+ * Settings.apk, however, is not part of CSI, which needs to be
+ * standalone (bootable and testable).
+ */
+public class NullHome extends Activity {
+ private static final String TAG = "NullHome";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
+ setContentView(R.layout.null_home_finishing_boot);
+ }
+
+ protected void onDestroy() {
+ super.onDestroy();
+ Log.i(TAG, "onDestroy");
+ }
+}
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index bde6ed531353..8d9d6ee68c67 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,10 +22,4 @@
<!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
-
- <!-- Threshold in micro watts below which a charger is rated as "slow"; 1A @ 5V -->
- <integer name="config_chargingSlowlyThreshold">5000000</integer>
-
- <!-- Threshold in micro watts above which a charger is rated as "fast"; 1.5A @ 5V -->
- <integer name="config_chargingFastThreshold">7500000</integer>
</resources>
diff --git a/packages/SystemUI/res/layout/controls_icon.xml b/packages/SystemUI/res/layout/controls_icon.xml
new file mode 100644
index 000000000000..cc46ced0a538
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_icon.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="28dp"
+ android:layout_height="28dp"
+ android:scaleType="fitCenter"
+ android:layout_marginLeft="2dp"
+ android:layout_marginRight="2dp" />
diff --git a/packages/SystemUI/res/layout/controls_no_favorites.xml b/packages/SystemUI/res/layout/controls_no_favorites.xml
index 096f1f49aaba..3e0699dff197 100644
--- a/packages/SystemUI/res/layout/controls_no_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_no_favorites.xml
@@ -1,18 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
<merge
xmlns:android="http://schemas.android.com/apk/res/android">
- <TextView
- android:id="@+id/controls_title"
- android:text="@string/quick_controls_title"
+ <LinearLayout
+ android:id="@+id/controls_no_favorites_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:singleLine="true"
- android:gravity="center"
- android:textSize="25dp"
+ android:orientation="vertical"
android:paddingTop="40dp"
android:paddingBottom="40dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
- android:textColor="@*android:color/foreground_material_dark"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:background="@drawable/control_no_favorites_background"/>
+ android:background="@drawable/control_no_favorites_background">
+
+ <LinearLayout
+ android:id="@+id/controls_icon_row"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="horizontal"
+ android:paddingBottom="8dp" />
+
+ <TextView
+ android:id="@+id/controls_title"
+ android:text="@string/quick_controls_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:layout_gravity="center"
+ android:textSize="25sp"
+ android:textColor="@*android:color/foreground_material_dark"
+ android:fontFamily="@*android:string/config_headlineFontFamily" />
+ </LinearLayout>
</merge>
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index b2423b9bf252..85724a969fed 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -27,7 +27,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
-import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
@@ -47,7 +47,6 @@ public class AdminSecondaryLockScreenController {
private Handler mHandler;
private IKeyguardClient mClient;
private KeyguardSecurityCallback mKeyguardCallback;
- private SurfaceControl.Transaction mTransaction;
private final ServiceConnection mConnection = new ServiceConnection() {
@Override
@@ -84,13 +83,13 @@ public class AdminSecondaryLockScreenController {
}
@Override
- public void onSurfaceControlCreated(@Nullable SurfaceControl remoteSurfaceControl) {
+ public void onRemoteContentReady(
+ @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) {
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
}
- if (remoteSurfaceControl != null) {
- mTransaction.reparent(remoteSurfaceControl, mView.getSurfaceControl())
- .apply();
+ if (surfacePackage != null) {
+ mView.setChildSurfacePackage(surfacePackage);
} else {
dismiss(KeyguardUpdateMonitor.getCurrentUser());
}
@@ -138,11 +137,10 @@ public class AdminSecondaryLockScreenController {
public AdminSecondaryLockScreenController(Context context, ViewGroup parent,
KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback,
- Handler handler, SurfaceControl.Transaction transaction) {
+ Handler handler) {
mContext = context;
mHandler = handler;
mParent = parent;
- mTransaction = transaction;
mUpdateMonitor = updateMonitor;
mKeyguardCallback = callback;
mView = new AdminSecurityView(mContext, mSurfaceHolderCallback);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 29c67ae1b4a6..ba8a1a945a77 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -39,7 +39,6 @@ import android.util.Slog;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.SurfaceControl;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
@@ -149,8 +148,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
mViewConfiguration = ViewConfiguration.get(context);
mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this,
- mUpdateMonitor, mCallback, new Handler(Looper.myLooper()),
- new SurfaceControl.Transaction());
+ mUpdateMonitor, mCallback, new Handler(Looper.myLooper()));
}
public void setSecurityCallback(SecurityCallback callback) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 6a04583b70ba..e5f6d3c90eb7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -21,15 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.ACTION_USER_STOPPED;
import static android.content.Intent.ACTION_USER_UNLOCKED;
-import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
-import static android.os.BatteryManager.BATTERY_STATUS_FULL;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
-import static android.os.BatteryManager.EXTRA_HEALTH;
-import static android.os.BatteryManager.EXTRA_LEVEL;
-import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
-import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
-import static android.os.BatteryManager.EXTRA_PLUGGED;
-import static android.os.BatteryManager.EXTRA_STATUS;
import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM;
@@ -67,7 +59,6 @@ import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
import android.media.AudioManager;
-import android.os.BatteryManager;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IRemoteCallback;
@@ -94,6 +85,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
+import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.DejankUtils;
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
@@ -1059,29 +1051,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
MSG_TIMEZONE_UPDATE, intent.getStringExtra("time-zone"));
mHandler.sendMessage(msg);
} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
- final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
- final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
- final int level = intent.getIntExtra(EXTRA_LEVEL, 0);
- final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
- final int maxChargingMicroAmp = intent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1);
- int maxChargingMicroVolt = intent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1);
- final int maxChargingMicroWatt;
-
- if (maxChargingMicroVolt <= 0) {
- maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT;
- }
- if (maxChargingMicroAmp > 0) {
- // Calculating muW = muA * muV / (10^6 mu^2 / mu); splitting up the divisor
- // to maintain precision equally on both factors.
- maxChargingMicroWatt = (maxChargingMicroAmp / 1000)
- * (maxChargingMicroVolt / 1000);
- } else {
- maxChargingMicroWatt = -1;
- }
final Message msg = mHandler.obtainMessage(
- MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health,
- maxChargingMicroWatt));
+ MSG_BATTERY_UPDATE, new BatteryStatus(intent));
mHandler.sendMessage(msg);
} else if (Intent.ACTION_SIM_STATE_CHANGED.equals(action)) {
SimData args = SimData.fromIntent(intent);
@@ -1323,82 +1295,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- public static class BatteryStatus {
- public static final int CHARGING_UNKNOWN = -1;
- public static final int CHARGING_SLOWLY = 0;
- public static final int CHARGING_REGULAR = 1;
- public static final int CHARGING_FAST = 2;
-
- public final int status;
- public final int level;
- public final int plugged;
- public final int health;
- public final int maxChargingWattage;
-
- public BatteryStatus(int status, int level, int plugged, int health,
- int maxChargingWattage) {
- this.status = status;
- this.level = level;
- this.plugged = plugged;
- this.health = health;
- this.maxChargingWattage = maxChargingWattage;
- }
-
- /**
- * Determine whether the device is plugged in (USB, power, or wireless).
- *
- * @return true if the device is plugged in.
- */
- public boolean isPluggedIn() {
- return plugged == BatteryManager.BATTERY_PLUGGED_AC
- || plugged == BatteryManager.BATTERY_PLUGGED_USB
- || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
- }
-
- /**
- * Determine whether the device is plugged in (USB, power).
- *
- * @return true if the device is plugged in wired (as opposed to wireless)
- */
- public boolean isPluggedInWired() {
- return plugged == BatteryManager.BATTERY_PLUGGED_AC
- || plugged == BatteryManager.BATTERY_PLUGGED_USB;
- }
-
- /**
- * Whether or not the device is charged. Note that some devices never return 100% for
- * battery level, so this allows either battery level or status to determine if the
- * battery is charged.
- *
- * @return true if the device is charged
- */
- public boolean isCharged() {
- return status == BATTERY_STATUS_FULL || level >= 100;
- }
-
- /**
- * Whether battery is low and needs to be charged.
- *
- * @return true if battery is low
- */
- public boolean isBatteryLow() {
- return level < LOW_BATTERY_THRESHOLD;
- }
-
- public final int getChargingSpeed(int slowThreshold, int fastThreshold) {
- return maxChargingWattage <= 0 ? CHARGING_UNKNOWN :
- maxChargingWattage < slowThreshold ? CHARGING_SLOWLY :
- maxChargingWattage > fastThreshold ? CHARGING_FAST :
- CHARGING_REGULAR;
- }
-
- @Override
- public String toString() {
- return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged
- + ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}";
- }
- }
-
public static class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
private final Consumer<Integer> mStrongAuthRequiredChangedCallback;
@@ -2640,9 +2536,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
*/
private boolean refreshSimState(int subId, int slotId) {
final TelephonyManager tele =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
int state = (tele != null) ?
- tele.getSimState(slotId) : TelephonyManager.SIM_STATE_UNKNOWN;
+ tele.getSimState(slotId) : TelephonyManager.SIM_STATE_UNKNOWN;
SimData data = mSimDatas.get(subId);
final boolean changed;
if (data == null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 8e87b7ad45b9..49f72a925a0e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -23,6 +23,7 @@ import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.view.WindowManagerPolicyConstants;
+import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.statusbar.KeyguardIndicationController;
import java.util.TimeZone;
@@ -42,7 +43,7 @@ public class KeyguardUpdateMonitorCallback {
*
* @param status current battery status
*/
- public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) { }
+ public void onRefreshBatteryInfo(BatteryStatus status) { }
/**
* Called once per minute or when the time changes.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 5c65977c8929..8a492a83b3df 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -87,7 +87,7 @@ public class AuthContainerView extends LinearLayout
@VisibleForTesting @Nullable AuthBiometricView mBiometricView;
@VisibleForTesting @Nullable AuthCredentialView mCredentialView;
- private final ImageView mBackgroundView;
+ @VisibleForTesting final ImageView mBackgroundView;
@VisibleForTesting final ScrollView mBiometricScrollView;
private final View mPanelView;
@@ -333,6 +333,12 @@ public class AuthContainerView extends LinearLayout
throw new IllegalStateException("Unknown credential type: " + credentialType);
}
+ // The background is used for detecting taps / cancelling authentication. Since the
+ // credential view is full-screen and should not be canceled from background taps,
+ // disable it.
+ mBackgroundView.setOnClickListener(null);
+ mBackgroundView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+
mCredentialView.setContainerView(this);
mCredentialView.setEffectiveUserId(mEffectiveUserId);
mCredentialView.setCredentialType(credentialType);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 2f1e4b41bd2d..05838abe184a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,7 +16,6 @@
package com.android.systemui.bubbles;
-import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_BUBBLE;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
@@ -44,19 +43,13 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.UserIdInt;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.RemoteInput;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ShortcutManager;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -75,25 +68,33 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.ScreenshotHelper;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.DumpController;
+import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import com.android.systemui.bubbles.BubbleController.BubbleExpandListener;
+import com.android.systemui.bubbles.BubbleController.BubbleStateChangeListener;
+import com.android.systemui.bubbles.BubbleController.NotifCallback;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PinnedStackListenerForwarder;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
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.RemoteInputUriController;
import com.android.systemui.statusbar.policy.ZenModeController;
import java.io.FileDescriptor;
@@ -101,10 +102,8 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -116,7 +115,7 @@ import javax.inject.Singleton;
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
@Singleton
-public class BubbleController implements ConfigurationController.ConfigurationListener {
+public class BubbleController implements ConfigurationController.ConfigurationListener, Dumpable {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
@@ -140,14 +139,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private final Context mContext;
private final NotificationEntryManager mNotificationEntryManager;
+ private final NotifPipeline mNotifPipeline;
private final BubbleTaskStackListener mTaskStackListener;
private BubbleStateChangeListener mStateChangeListener;
private BubbleExpandListener mExpandListener;
@Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
private final NotificationGroupManager mNotificationGroupManager;
private final ShadeController mShadeController;
- private final RemoteInputUriController mRemoteInputUriController;
- private Handler mHandler = new Handler() {};
private BubbleData mBubbleData;
@Nullable private BubbleStackView mStackView;
@@ -171,7 +169,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final ZenModeController mZenModeController;
private StatusBarStateListener mStatusBarStateListener;
- private final ScreenshotHelper mScreenshotHelper;
// Callback that updates BubbleOverflowActivity on data change.
@Nullable private Runnable mOverflowCallback = null;
@@ -217,16 +214,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/**
- * Listener for handling bubble screenshot events.
- */
- public interface BubbleScreenshotListener {
- /**
- * Called to trigger taking a screenshot and sending the result to a bubble.
- */
- void onBubbleScreenshot(Bubble bubble);
- }
-
- /**
* Listener to be notified when a bubbles' notification suppression state changes.
*/
public interface NotificationSuppressionChangedListener {
@@ -243,16 +230,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
*/
public interface NotifCallback {
/**
- * Called when the BubbleController wants to remove an entry that it was previously hiding
- * from the shade. See {@link BubbleController#isBubbleNotificationSuppressedFromShade}.
+ * Called when a bubbled notification that was hidden from the shade is now being removed
+ * This can happen when an app cancels a bubbled notification or when the user dismisses a
+ * bubble.
*/
- void removeNotification(NotificationEntry entry);
+ void removeNotification(NotificationEntry entry, int reason);
/**
* Called when a bubbled notification has changed whether it should be
* filtered from the shade.
*/
- void invalidateNotificationFilter(String reason);
+ void invalidateNotifications(String reason);
/**
* Called on a bubbled entry that has been removed when there are no longer
@@ -301,11 +289,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManager groupManager,
NotificationEntryManager entryManager,
- RemoteInputUriController remoteInputUriController) {
+ NotifPipeline notifPipeline,
+ FeatureFlags featureFlags,
+ DumpController dumpController) {
this(context, notificationShadeWindowController, statusBarStateController, shadeController,
data, null /* synchronizer */, configurationController, interruptionStateProvider,
zenModeController, notifUserManager, groupManager, entryManager,
- remoteInputUriController);
+ notifPipeline, featureFlags, dumpController);
}
public BubbleController(Context context,
@@ -320,13 +310,15 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManager groupManager,
NotificationEntryManager entryManager,
- RemoteInputUriController remoteInputUriController) {
+ NotifPipeline notifPipeline,
+ FeatureFlags featureFlags,
+ DumpController dumpController) {
+ dumpController.registerDumpable(TAG, this);
mContext = context;
mShadeController = shadeController;
mNotificationInterruptionStateProvider = interruptionStateProvider;
mNotifUserManager = notifUserManager;
mZenModeController = zenModeController;
- mRemoteInputUriController = remoteInputUriController;
mZenModeController.addCallback(new ZenModeController.Callback() {
@Override
public void onZenChanged(int zen) {
@@ -364,7 +356,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mNotificationEntryManager = entryManager;
mNotificationGroupManager = groupManager;
- setupNEM();
+ mNotifPipeline = notifPipeline;
+
+ if (!featureFlags.isNewNotifPipelineRenderingEnabled()) {
+ setupNEM();
+ } else {
+ setupNotifPipeline();
+ }
mNotificationShadeWindowController = notificationShadeWindowController;
mStatusBarStateListener = new StatusBarStateListener();
@@ -399,7 +397,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mUserCreatedBubbles = new HashSet<>();
mUserBlockedBubbles = new HashSet<>();
- mScreenshotHelper = new ScreenshotHelper(context);
mBubbleIconFactory = new BubbleIconFactory(context);
}
@@ -424,6 +421,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
@Override
+ public void onEntryRemoved(
+ NotificationEntry entry,
+ @android.annotation.Nullable NotificationVisibility visibility,
+ boolean removedByUser) {
+ BubbleController.this.onEntryRemoved(entry);
+ }
+
+ @Override
public void onNotificationRankingUpdated(RankingMap rankingMap) {
onRankingUpdated(rankingMap);
}
@@ -433,8 +438,29 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
new NotificationRemoveInterceptor() {
@Override
public boolean onNotificationRemoveRequested(
- String key, NotificationEntry entry, int reason) {
- return shouldInterceptDismissal(entry, reason);
+ String key,
+ NotificationEntry entry,
+ int dismissReason) {
+ final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
+ final boolean isUserDimiss = dismissReason == REASON_CANCEL
+ || dismissReason == REASON_CLICK;
+ final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
+ || dismissReason == REASON_APP_CANCEL_ALL;
+ final boolean isSummaryCancel =
+ dismissReason == REASON_GROUP_SUMMARY_CANCELED;
+
+ // Need to check for !appCancel here because the notification may have
+ // previously been dismissed & entry.isRowDismissed would still be true
+ boolean userRemovedNotif =
+ (entry != null && entry.isRowDismissed() && !isAppCancel)
+ || isClearAll || isUserDimiss || isSummaryCancel;
+
+ if (userRemovedNotif || isUserCreatedBubble(key)
+ || isSummaryOfUserCreatedBubble(entry)) {
+ return handleDismissalInterception(entry);
+ }
+
+ return false;
}
});
@@ -458,13 +484,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
addNotifCallback(new NotifCallback() {
@Override
- public void removeNotification(NotificationEntry entry) {
+ public void removeNotification(NotificationEntry entry, int reason) {
mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
- UNDEFINED_DISMISS_REASON);
+ reason);
}
@Override
- public void invalidateNotificationFilter(String reason) {
+ public void invalidateNotifications(String reason) {
mNotificationEntryManager.updateNotifications(reason);
}
@@ -472,18 +498,28 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
public void maybeCancelSummary(NotificationEntry entry) {
// Check if removed bubble has an associated suppressed group summary that needs
// to be removed now.
- final String groupKey = entry.getSbn().getGroup();
+ final String groupKey = entry.getSbn().getGroupKey();
if (mBubbleData.isSummarySuppressed(groupKey)) {
- mBubbleData.removeSuppressedSummary(entry.getSbn().getGroupKey());
+ mBubbleData.removeSuppressedSummary(groupKey);
final NotificationEntry summary =
mNotificationEntryManager.getActiveNotificationUnfiltered(
mBubbleData.getSummaryKey(groupKey));
- mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
- UNDEFINED_DISMISS_REASON);
+ if (summary != null) {
+ mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
+ UNDEFINED_DISMISS_REASON);
+ }
}
- // Check if summary should be removed from NoManGroup
+ // Check if we still need to remove the summary from NoManGroup because the summary
+ // may not be in the mBubbleData.mSuppressedGroupKeys list and removed above.
+ // For example:
+ // 1. Bubbled notifications (group) is posted to shade and are visible bubbles
+ // 2. User expands bubbles so now their respective notifications in the shade are
+ // hidden, including the group summary
+ // 3. User removes all bubbles
+ // 4. We expect all the removed bubbles AND the summary (note: the summary was
+ // never added to the suppressedSummary list in BubbleData, so we add this check)
NotificationEntry summary =
mNotificationGroupManager.getLogicalGroupSummary(entry.getSbn());
if (summary != null) {
@@ -500,6 +536,31 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
});
}
+ private void setupNotifPipeline() {
+ mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
+ @Override
+ public void onEntryAdded(NotificationEntry entry) {
+ BubbleController.this.onEntryAdded(entry);
+ }
+
+ @Override
+ public void onEntryUpdated(NotificationEntry entry) {
+ BubbleController.this.onEntryUpdated(entry);
+ }
+
+ @Override
+ public void onRankingUpdate(RankingMap rankingMap) {
+ onRankingUpdated(rankingMap);
+ }
+
+ @Override
+ public void onEntryRemoved(NotificationEntry entry,
+ @NotifCollection.CancellationReason int reason) {
+ BubbleController.this.onEntryRemoved(entry);
+ }
+ });
+ }
+
/**
* Sets whether to perform inflation on the same thread as the caller. This method should only
* be used in tests, not in production.
@@ -536,9 +597,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (mExpandListener != null) {
mStackView.setExpandListener(mExpandListener);
}
- if (mBubbleScreenshotListener != null) {
- mStackView.setBubbleScreenshotListener(mBubbleScreenshotListener);
- }
}
}
@@ -783,7 +841,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
Log.d(TAG, "onUserDemotedBubble: " + entry.getKey());
}
entry.setFlagBubble(false);
- removeBubble(entry.getKey(), DISMISS_BLOCKED);
+ removeBubble(entry, DISMISS_BLOCKED);
mUserCreatedBubbles.remove(entry.getKey());
if (BubbleExperimentConfig.isPackageWhitelistedToAutoBubble(
mContext, entry.getSbn().getPackageName())) {
@@ -800,17 +858,29 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
return mUserCreatedBubbles.contains(key);
}
+ boolean isSummaryOfUserCreatedBubble(NotificationEntry entry) {
+ if (isSummaryOfBubbles(entry)) {
+ List<Bubble> bubbleChildren =
+ mBubbleData.getBubblesInGroup(entry.getSbn().getGroupKey());
+ for (int i = 0; i < bubbleChildren.size(); i++) {
+ // Check if any are user-created (i.e. experimental bubbles)
+ if (isUserCreatedBubble(bubbleChildren.get(i).getKey())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
- * Removes the bubble associated with the {@param uri}.
+ * Removes the bubble with the given NotificationEntry.
* <p>
* Must be called from the main thread.
*/
@MainThread
- void removeBubble(String key, int reason) {
- // TEMP: refactor to change this to pass entry
- Bubble bubble = mBubbleData.getBubbleWithKey(key);
- if (bubble != null) {
- mBubbleData.notificationEntryRemoved(bubble.getEntry(), reason);
+ void removeBubble(NotificationEntry entry, int reason) {
+ if (mBubbleData.hasBubbleWithKey(entry.getKey())) {
+ mBubbleData.notificationEntryRemoved(entry, reason);
}
}
@@ -840,7 +910,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
&& (canLaunchInActivityView(mContext, entry) || wasAdjusted);
if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
- removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
+ removeBubble(entry, DISMISS_NO_LONGER_BUBBLE);
} else if (shouldBubble) {
if (wasAdjusted && !previouslyUserCreated) {
// Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
@@ -850,6 +920,21 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
+ private void onEntryRemoved(NotificationEntry entry) {
+ if (isSummaryOfBubbles(entry)) {
+ final String groupKey = entry.getSbn().getGroupKey();
+ mBubbleData.removeSuppressedSummary(groupKey);
+
+ // Remove any associated bubble children with the summary
+ final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
+ for (int i = 0; i < bubbleChildren.size(); i++) {
+ removeBubble(bubbleChildren.get(i).getEntry(), DISMISS_GROUP_CANCELLED);
+ }
+ } else {
+ removeBubble(entry, DISMISS_NOTIF_CANCEL);
+ }
+ }
+
private void onRankingUpdated(RankingMap rankingMap) {
// Forward to BubbleData to block any bubbles which should no longer be shown
mBubbleData.notificationRankingUpdated(rankingMap);
@@ -877,7 +962,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
final Bubble bubble = removed.first;
@DismissReason final int reason = removed.second;
mStackView.removeBubble(bubble);
-
// If the bubble is removed for user switching, leave the notification in place.
if (reason != DISMISS_USER_CHANGED) {
if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
@@ -885,7 +969,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// The bubble is now gone & the notification is hidden from the shade, so
// time to actually remove it
for (NotifCallback cb : mCallbacks) {
- cb.removeNotification(bubble.getEntry());
+ cb.removeNotification(bubble.getEntry(), REASON_CANCEL);
}
} else {
// Update the flag for SysUI
@@ -939,7 +1023,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
for (NotifCallback cb : mCallbacks) {
- cb.invalidateNotificationFilter("BubbleData.Listener.applyUpdate");
+ cb.invalidateNotifications("BubbleData.Listener.applyUpdate");
}
updateStack();
@@ -961,124 +1045,85 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
};
/**
- * We intercept notification entries cancelled by the user (i.e. dismissed) when there is an
- * active bubble associated with it. We do this so that developers can still cancel it
- * (and hence the bubbles associated with it). However, these intercepted notifications
- * should then be hidden from the shade since the user has cancelled them, so we update
- * {@link Bubble#showInShade}.
- *
- * The cancellation of summaries with children associated with bubbles are also handled in this
- * method. User-cancelled summaries are tracked by {@link BubbleData#addSummaryToSuppress}.
+ * We intercept notification entries (including group summaries) dismissed by the user when
+ * there is an active bubble associated with it. We do this so that developers can still
+ * cancel it (and hence the bubbles associated with it). However, these intercepted
+ * notifications should then be hidden from the shade since the user has cancelled them, so we
+ * {@link Bubble#setSuppressNotification}. For the case of suppressed summaries, we also add
+ * {@link BubbleData#addSummaryToSuppress}.
*
* @return true if we want to intercept the dismissal of the entry, else false.
*/
- public boolean shouldInterceptDismissal(NotificationEntry entry, int dismissReason) {
+ public boolean handleDismissalInterception(NotificationEntry entry) {
if (entry == null) {
return false;
}
- String key = entry.getKey();
- String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
- ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
- boolean inBubbleData = mBubbleData.hasBubbleWithKey(key);
- boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
- && mBubbleData.getSummaryKey(groupKey).equals(key));
- boolean isSummary = entry != null
- && entry.getSbn().getNotification().isGroupSummary();
- boolean isSummaryOfBubbles = (isSuppressedSummary || isSummary)
- && bubbleChildren != null && !bubbleChildren.isEmpty();
+ final boolean interceptBubbleDismissal = mBubbleData.hasBubbleWithKey(entry.getKey())
+ && entry.isBubble();
+ final boolean interceptSummaryDismissal = isSummaryOfBubbles(entry);
- if (!inBubbleData && !isSummaryOfBubbles) {
+ if (interceptSummaryDismissal) {
+ handleSummaryDismissalInterception(entry);
+ } else if (interceptBubbleDismissal) {
+ Bubble bubble = mBubbleData.getBubbleWithKey(entry.getKey());
+ bubble.setSuppressNotification(true);
+ bubble.setShowDot(false /* show */, true /* animate */);
+ } else {
return false;
}
- final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
- final boolean isUserDimiss = dismissReason == REASON_CANCEL
- || dismissReason == REASON_CLICK;
- final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
- || dismissReason == REASON_APP_CANCEL_ALL;
- final boolean isSummaryCancel = dismissReason == REASON_GROUP_SUMMARY_CANCELED;
-
- // Need to check for !appCancel here because the notification may have
- // previously been dismissed & entry.isRowDismissed would still be true
- boolean userRemovedNotif = (entry != null && entry.isRowDismissed() && !isAppCancel)
- || isClearAll || isUserDimiss || isSummaryCancel;
- if (isSummaryOfBubbles) {
- return handleSummaryRemovalInterception(entry, userRemovedNotif);
+ // Update the shade
+ for (NotifCallback cb : mCallbacks) {
+ cb.invalidateNotifications("BubbleController.handleDismissalInterception");
}
+ return true;
+ }
- // The bubble notification sticks around in the data as long as the bubble is
- // not dismissed and the app hasn't cancelled the notification.
- Bubble bubble = mBubbleData.getBubbleWithKey(key);
- boolean bubbleExtended = entry != null && entry.isBubble()
- && (userRemovedNotif || isUserCreatedBubble(bubble.getKey()));
- if (bubbleExtended) {
- bubble.setSuppressNotification(true);
- bubble.setShowDot(false /* show */, true /* animate */);
- for (NotifCallback cb : mCallbacks) {
- cb.invalidateNotificationFilter("BubbleController"
- + ".shouldInterceptDismissal");
- }
- return true;
- } else if (!userRemovedNotif && entry != null) {
- // This wasn't a user removal so we should remove the bubble as well
- mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
+ private boolean isSummaryOfBubbles(NotificationEntry entry) {
+ if (entry == null) {
return false;
}
- return false;
- }
- private boolean handleSummaryRemovalInterception(NotificationEntry summary,
- boolean userRemovedNotif) {
- String groupKey = summary.getSbn().getGroupKey();
+ String groupKey = entry.getSbn().getGroupKey();
ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
+ boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
+ && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey()));
+ boolean isSummary = entry.getSbn().getNotification().isGroupSummary();
+ return (isSuppressedSummary || isSummary)
+ && bubbleChildren != null
+ && !bubbleChildren.isEmpty();
+ }
- if (userRemovedNotif) {
- // If it's a user dismiss we mark the children to be hidden from the shade.
- for (int i = 0; i < bubbleChildren.size(); i++) {
- Bubble bubbleChild = bubbleChildren.get(i);
- // As far as group manager is concerned, once a child is no longer shown
- // in the shade, it is essentially removed.
- mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
- bubbleChild.setSuppressNotification(true);
- bubbleChild.setShowDot(false /* show */, true /* animate */);
- }
- // And since all children are removed, remove the summary.
- mNotificationGroupManager.onEntryRemoved(summary);
-
- // If the summary was auto-generated we don't need to keep that notification around
- // because apps can't cancel it; so we only intercept & suppress real summaries.
- boolean isAutogroupSummary = (summary.getSbn().getNotification().flags
- & FLAG_AUTOGROUP_SUMMARY) != 0;
- if (!isAutogroupSummary) {
- // TODO: (b/145659174) remove references to mSuppressedGroupKeys once fully migrated
- mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(),
- summary.getKey());
- // Tell shade to update for the suppression
- mNotificationEntryManager.updateNotifications("BubbleController"
- + ".handleSummaryRemovalInterception");
- }
- return !isAutogroupSummary;
- } else {
- // If it's not a user dismiss it's a cancel.
- for (int i = 0; i < bubbleChildren.size(); i++) {
- // First check if any of these are user-created (i.e. experimental bubbles)
- if (mUserCreatedBubbles.contains(bubbleChildren.get(i).getKey())) {
- // Experimental bubble! Intercept the removal.
- return true;
+ private void handleSummaryDismissalInterception(NotificationEntry summary) {
+ // current children in the row:
+ final List<NotificationEntry> children = summary.getChildren();
+ if (children != null) {
+ for (int i = 0; i < children.size(); i++) {
+ NotificationEntry child = children.get(i);
+ if (mBubbleData.hasBubbleWithKey(child.getKey())) {
+ // Suppress the bubbled child
+ // As far as group manager is concerned, once a child is no longer shown
+ // in the shade, it is essentially removed.
+ Bubble bubbleChild = mBubbleData.getBubbleWithKey(child.getKey());
+ mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
+ bubbleChild.setSuppressNotification(true);
+ bubbleChild.setShowDot(false /* show */, true /* animate */);
+ } else {
+ // non-bubbled children can be removed
+ for (NotifCallback cb : mCallbacks) {
+ cb.removeNotification(child, REASON_GROUP_SUMMARY_CANCELED);
+ }
}
}
-
- // Not an experimental bubble, safe to remove.
- mBubbleData.removeSuppressedSummary(groupKey);
- // Remove any associated bubble children with the summary.
- for (int i = 0; i < bubbleChildren.size(); i++) {
- Bubble bubbleChild = bubbleChildren.get(i);
- mBubbleData.notificationEntryRemoved(bubbleChild.getEntry(),
- DISMISS_GROUP_CANCELLED);
- }
- return false;
}
+
+ // And since all children are removed, remove the summary.
+ mNotificationGroupManager.onEntryRemoved(summary);
+
+ // TODO: (b/145659174) remove references to mSuppressedGroupKeys once fully migrated
+ mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(),
+ summary.getKey());
}
/**
@@ -1267,68 +1312,4 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
}
-
- // TODO: Copied from RemoteInputView. Consolidate RemoteInput intent logic.
- private Intent prepareRemoteInputFromData(String contentType, Uri data,
- RemoteInput remoteInput, NotificationEntry entry) {
- HashMap<String, Uri> results = new HashMap<>();
- results.put(contentType, data);
- mRemoteInputUriController.grantInlineReplyUriPermission(entry.getSbn(), data);
- Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- RemoteInput.addDataResultToIntent(remoteInput, fillInIntent, results);
-
- return fillInIntent;
- }
-
- // TODO: Copied from RemoteInputView. Consolidate RemoteInput intent logic.
- private void sendRemoteInput(Intent intent, NotificationEntry entry,
- PendingIntent pendingIntent) {
- // Tell ShortcutManager that this package has been "activated". ShortcutManager
- // will reset the throttling for this package.
- // Strictly speaking, the intent receiver may be different from the notification publisher,
- // but that's an edge case, and also because we can't always know which package will receive
- // an intent, so we just reset for the publisher.
- mContext.getSystemService(ShortcutManager.class).onApplicationActive(
- entry.getSbn().getPackageName(),
- entry.getSbn().getUser().getIdentifier());
-
- try {
- pendingIntent.send(mContext, 0, intent);
- } catch (PendingIntent.CanceledException e) {
- Log.i(TAG, "Unable to send remote input result", e);
- }
- }
-
- private void sendScreenshotToBubble(Bubble bubble) {
- mScreenshotHelper.takeScreenshot(
- android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
- true /* hasStatus */,
- true /* hasNav */,
- mHandler,
- new Consumer<Uri>() {
- @Override
- public void accept(Uri uri) {
- if (uri != null) {
- NotificationEntry entry = bubble.getEntry();
- Pair<RemoteInput, Notification.Action> pair = entry.getSbn()
- .getNotification().findRemoteInputActionPair(false);
- if (pair != null) {
- RemoteInput remoteInput = pair.first;
- Notification.Action action = pair.second;
- Intent dataIntent = prepareRemoteInputFromData("image/png", uri,
- remoteInput, entry);
- sendRemoteInput(dataIntent, entry, action.actionIntent);
- mBubbleData.setSelectedBubble(bubble);
- mBubbleData.setExpanded(true);
- } else {
- Log.w(TAG, "No RemoteInput found for notification: "
- + entry.getSbn().getKey());
- }
- }
- }
- });
- }
-
- private final BubbleScreenshotListener mBubbleScreenshotListener =
- bubble -> sendScreenshotToBubble(bubble);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 50a50633f43c..0d5261dcb7f3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -19,9 +19,9 @@ package com.android.systemui.bubbles;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.view.Display.INVALID_DISPLAY;
-
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.sNewInsetsMode;
+
import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -56,6 +56,7 @@ import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.AlphaOptimizedButton;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* Container for the expanded bubble view, handles rendering the caret and settings icon.
@@ -146,7 +147,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
// the bubble again so we'll just remove it.
Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
+ ", " + e.getMessage() + "; removing bubble");
- mBubbleController.removeBubble(getBubbleKey(),
+ mBubbleController.removeBubble(getBubbleEntry(),
BubbleController.DISMISS_INVALID_INTENT);
}
});
@@ -190,7 +191,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
}
if (mBubble != null && !mBubbleController.isUserCreatedBubble(mBubble.getKey())) {
// Must post because this is called from a binder thread.
- post(() -> mBubbleController.removeBubble(mBubble.getKey(),
+ post(() -> mBubbleController.removeBubble(mBubble.getEntry(),
BubbleController.DISMISS_TASK_FINISHED));
}
}
@@ -279,6 +280,10 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
return mBubble != null ? mBubble.getKey() : "null";
}
+ private NotificationEntry getBubbleEntry() {
+ return mBubble != null ? mBubble.getEntry() : null;
+ }
+
void applyThemeAttrs() {
final TypedArray ta = mContext.obtainStyledAttributes(
new int[] {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 006de8406ce2..20b3386b450d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -73,9 +73,6 @@ public class BubbleExperimentConfig {
private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps";
- private static final String ALLOW_BUBBLE_MENU = "allow_bubble_screenshot_menu";
- private static final boolean ALLOW_BUBBLE_MENU_DEFAULT = false;
-
private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow";
private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = false;
@@ -137,16 +134,6 @@ public class BubbleExperimentConfig {
* When true, show a menu when a bubble is long-pressed, which will allow the user to take
* actions on that bubble.
*/
- static boolean allowBubbleScreenshotMenu(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- ALLOW_BUBBLE_MENU,
- ALLOW_BUBBLE_MENU_DEFAULT ? 1 : 0) != 0;
- }
-
- /**
- * When true, show a menu when a bubble is long-pressed, which will allow the user to take
- * actions on that bubble.
- */
static boolean allowBubbleOverflow(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
ALLOW_BUBBLE_OVERFLOW,
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleMenuView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleMenuView.java
deleted file mode 100644
index bf8306561f1b..000000000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleMenuView.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.bubbles;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-/**
- * Menu which allows users to take actions on bubbles, ex. screenshots.
- */
-public class BubbleMenuView extends FrameLayout {
- private FrameLayout mMenu;
- private boolean mShowing = false;
-
- /** Delay before taking a screenshot once the button is tapped to allow the menu time to hide.*/
- public static final long SCREENSHOT_DELAY = 200;
-
- public BubbleMenuView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public BubbleMenuView(Context context) {
- super(context);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mMenu = findViewById(R.id.bubble_menu_view);
- ImageView icon = findViewById(com.android.internal.R.id.icon);
- icon.setImageDrawable(mContext.getDrawable(com.android.internal.R.drawable.ic_screenshot));
- }
-
- /**
- * Get the bubble menu view.
- */
- public View getMenuView() {
- return mMenu;
- }
-
- /**
- * Checks whether the bubble menu is currently displayed.
- */
- public boolean isShowing() {
- return mShowing;
- }
-
- /**
- * Show the bubble menu at the specified position on the screen.
- */
- public void show(float x, float y) {
- mShowing = true;
- this.setVisibility(VISIBLE);
- mMenu.setTranslationX(x);
- mMenu.setTranslationY(y);
- }
-
- /**
- * Hide the bubble menu.
- */
- public void hide() {
- mShowing = false;
- this.setVisibility(GONE);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 20d19ece575c..bce172b89187 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -115,7 +115,6 @@ public class BubbleStackView extends FrameLayout {
/** How long to wait, in milliseconds, before hiding the flyout. */
@VisibleForTesting
static final int FLYOUT_HIDE_AFTER = 5000;
- private BubbleController.BubbleScreenshotListener mBubbleScreenshotListener;
/**
* Interface to synchronize {@link View} state and the screen.
@@ -169,7 +168,6 @@ public class BubbleStackView extends FrameLayout {
private ExpandedAnimationController mExpandedAnimationController;
private FrameLayout mExpandedViewContainer;
- @Nullable private BubbleMenuView mBubbleMenuView;
private BubbleFlyoutView mFlyout;
/** Runnable that fades out the flyout and then sets it to GONE. */
@@ -516,9 +514,6 @@ public class BubbleStackView extends FrameLayout {
mDesaturateAndDarkenPaint.setColorFilter(new ColorMatrixColorFilter(animatedMatrix));
mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint);
});
-
- mInflater.inflate(R.layout.bubble_menu_view, this);
- mBubbleMenuView = findViewById(R.id.bubble_menu_container);
}
private void setUpOverflow() {
@@ -738,13 +733,6 @@ public class BubbleStackView extends FrameLayout {
}
/**
- * Sets the screenshot listener.
- */
- public void setBubbleScreenshotListener(BubbleController.BubbleScreenshotListener listener) {
- mBubbleScreenshotListener = listener;
- }
-
- /**
* Whether the stack of bubbles is expanded or not.
*/
public boolean isExpanded() {
@@ -942,12 +930,6 @@ public class BubbleStackView extends FrameLayout {
public View getTargetView(MotionEvent event) {
float x = event.getRawX();
float y = event.getRawY();
- if (mBubbleMenuView.isShowing()) {
- if (isIntersecting(mBubbleMenuView.getMenuView(), x, y)) {
- return mBubbleMenuView;
- }
- return null;
- }
if (mIsExpanded) {
if (isIntersecting(mBubbleContainer, x, y)) {
if (BubbleExperimentConfig.allowBubbleOverflow(mContext)
@@ -1168,7 +1150,6 @@ public class BubbleStackView extends FrameLayout {
}
return;
}
- hideBubbleMenu();
mStackAnimationController.cancelStackPositionAnimations();
mBubbleContainer.setActiveController(mStackAnimationController);
hideFlyoutImmediate();
@@ -1570,10 +1551,6 @@ public class BubbleStackView extends FrameLayout {
@Override
public void getBoundsOnScreen(Rect outRect) {
// If the bubble menu is open, the entire screen should capture touch events.
- if (mBubbleMenuView.isShowing()) {
- outRect.set(0, 0, getWidth(), getHeight());
- return;
- }
if (!mIsExpanded) {
if (getBubbleCount() > 0) {
mBubbleContainer.getChildAt(0).getBoundsOnScreen(outRect);
@@ -1808,50 +1785,4 @@ public class BubbleStackView extends FrameLayout {
}
return bubbles;
}
-
- /**
- * Show the bubble menu, positioned relative to the stack.
- */
- public void showBubbleMenu() {
- PointF currentPos = mStackAnimationController.getStackPosition();
- mBubbleMenuView.setVisibility(View.INVISIBLE);
- post(() -> {
- float yPos = currentPos.y;
- float xPos = currentPos.x;
- if (mStackAnimationController.isStackOnLeftSide()) {
- xPos += mBubbleSize;
- } else {
- xPos -= mBubbleMenuView.getMenuView().getWidth();
- }
-
- mBubbleMenuView.show(xPos, yPos);
- });
- }
-
- /**
- * Hide the bubble menu.
- */
- public void hideBubbleMenu() {
- mBubbleMenuView.hide();
- }
-
- /**
- * Determines whether the bubble menu is currently showing.
- */
- public boolean isShowingBubbleMenu() {
- return mBubbleMenuView.isShowing();
- }
-
- /**
- * Take a screenshot and send it to the specified bubble.
- */
- public void sendScreenshotToBubble(Bubble bubble) {
- hideBubbleMenu();
- // delay allows the bubble menu to disappear before the screenshot
- // done here because we already have a Handler to delay with.
- // TODO: Hide bubble + menu UI from screenshots entirely instead of just delaying.
- postDelayed(() -> {
- mBubbleScreenshotListener.onBubbleScreenshot(bubble);
- }, BubbleMenuView.SCREENSHOT_DELAY);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 5a9d44b6da2c..645696d0bcac 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -58,14 +58,12 @@ class BubbleTouchHandler implements View.OnTouchListener {
private final PointF mViewPositionOnTouchDown = new PointF();
private final BubbleStackView mStack;
private final BubbleData mBubbleData;
- private final Context mContext;
private BubbleController mController = Dependency.get(BubbleController.class);
private boolean mMovedEnough;
private int mTouchSlopSquared;
private VelocityTracker mVelocityTracker;
- private Runnable mShowBubbleMenuRunnable;
/** View that was initially touched, when we received the first ACTION_DOWN event. */
private View mTouchedView;
@@ -78,7 +76,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
mTouchSlopSquared = touchSlop * touchSlop;
mBubbleData = bubbleData;
mStack = stackView;
- mContext = context;
}
@Override
@@ -95,18 +92,10 @@ class BubbleTouchHandler implements View.OnTouchListener {
// anything, collapse the stack.
if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
mBubbleData.setExpanded(false);
- mStack.hideBubbleMenu();
resetForNextGesture();
return false;
}
- if (mTouchedView instanceof BubbleMenuView) {
- mStack.hideBubbleMenu();
- resetForNextGesture();
- mStack.sendScreenshotToBubble(mBubbleData.getSelectedBubble());
- return false;
- }
-
if (!(mTouchedView instanceof BadgedImageView)
&& !(mTouchedView instanceof BubbleStackView)
&& !(mTouchedView instanceof BubbleFlyoutView)) {
@@ -116,7 +105,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
}
// Not touching anything touchable, but we shouldn't collapse (e.g. touching edge
// of expanded view).
- mStack.hideBubbleMenu();
resetForNextGesture();
return false;
}
@@ -139,12 +127,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
if (isStack) {
mViewPositionOnTouchDown.set(mStack.getStackPosition());
mStack.onDragStart();
- if (!mStack.isShowingBubbleMenu() && !mStack.isExpanded()
- && BubbleExperimentConfig.allowBubbleScreenshotMenu(mContext)) {
- mShowBubbleMenuRunnable = mStack::showBubbleMenu;
- mStack.postDelayed(mShowBubbleMenuRunnable,
- ViewConfiguration.getLongPressTimeout());
- }
} else if (isFlyout) {
mStack.onFlyoutDragStart();
} else {
@@ -155,10 +137,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
break;
case MotionEvent.ACTION_MOVE:
- // block all further touch inputs once the menu is open
- if (mStack.isShowingBubbleMenu()) {
- return true;
- }
trackMovement(event);
final float deltaX = rawX - mTouchDown.x;
final float deltaY = rawY - mTouchDown.y;
@@ -168,7 +146,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
}
if (mMovedEnough) {
- mStack.removeCallbacks(mShowBubbleMenuRunnable);
if (isStack) {
mStack.onDragged(viewX, viewY);
} else if (isFlyout) {
@@ -199,12 +176,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
break;
case MotionEvent.ACTION_UP:
- if (mStack.isShowingBubbleMenu()) {
- resetForNextGesture();
- return true;
- } else {
- mStack.removeCallbacks(mShowBubbleMenuRunnable);
- }
trackMovement(event);
mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000);
final float velX = mVelocityTracker.getXVelocity();
@@ -227,9 +198,14 @@ class BubbleTouchHandler implements View.OnTouchListener {
if (isStack) {
mController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
} else {
- mController.removeBubble(
- individualBubbleKey,
- BubbleController.DISMISS_USER_GESTURE);
+ final Bubble bubble =
+ mBubbleData.getBubbleWithKey(individualBubbleKey);
+ // bubble can be null if the user is in the middle of
+ // dismissing the bubble, but the app also sent a cancel
+ if (bubble != null) {
+ mController.removeBubble(bubble.getEntry(),
+ BubbleController.DISMISS_USER_GESTURE);
+ }
}
});
} else if (isFlyout) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 88b19b58a453..78e0e8b81b44 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -39,7 +39,7 @@ import com.android.systemui.R
const val MIN_LEVEL = 0
const val MAX_LEVEL = 10000
-private const val UPDATE_DELAY_IN_MILLIS = 2000L
+private const val UPDATE_DELAY_IN_MILLIS = 3000L
class ControlViewHolder(
val layout: ViewGroup,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index ed521e3be535..f029dfbe1bb2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -22,6 +22,7 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
+import android.graphics.drawable.Drawable
import android.os.IBinder
import android.service.controls.Control
import android.service.controls.TokenProvider
@@ -29,19 +30,24 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Space
-import android.widget.TextView
+import com.android.settingslib.widget.CandidateInfo
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.controller.ControlInfo
+import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.management.ControlsProviderSelectorActivity
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.R
import com.android.systemui.util.concurrency.DelayableExecutor
import dagger.Lazy
+import java.text.Collator
+
import javax.inject.Inject
import javax.inject.Singleton
@@ -110,7 +116,9 @@ private data class ControlKey(val componentName: ComponentName, val controlId: S
class ControlsUiControllerImpl @Inject constructor (
val controlsController: Lazy<ControlsController>,
val context: Context,
- @Main val uiExecutor: DelayableExecutor
+ @Main val uiExecutor: DelayableExecutor,
+ @Background val bgExecutor: DelayableExecutor,
+ val controlsListingController: Lazy<ControlsListingController>
) : ControlsUiController {
private lateinit var controlInfos: List<ControlInfo>
@@ -121,6 +129,22 @@ class ControlsUiControllerImpl @Inject constructor (
override val available: Boolean
get() = controlsController.get().available
+ private val listingCallback = object : ControlsListingController.ControlsListingCallback {
+ override fun onServicesUpdated(candidates: List<CandidateInfo>) {
+ bgExecutor.execute {
+ val collator = Collator.getInstance(context.getResources()
+ .getConfiguration().locale)
+ val localeComparator = compareBy<CandidateInfo, CharSequence>(collator) {
+ it.loadLabel()
+ }
+
+ val mList = candidates.toMutableList()
+ mList.sortWith(localeComparator)
+ loadInitialSetupViewIcons(mList.map { it.loadLabel() to it.loadIcon() })
+ }
+ }
+ }
+
override fun show(parent: ViewGroup) {
Log.d(TAG, "show()")
@@ -153,8 +177,26 @@ class ControlsUiControllerImpl @Inject constructor (
val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.controls_no_favorites, parent, true)
- val textView = parent.requireViewById(R.id.controls_title) as TextView
- textView.setOnClickListener(launchSelectorActivityListener(context))
+ val viewGroup = parent.requireViewById(R.id.controls_no_favorites_group) as ViewGroup
+ viewGroup.setOnClickListener(launchSelectorActivityListener(context))
+
+ controlsListingController.get().addCallback(listingCallback)
+ }
+
+ private fun loadInitialSetupViewIcons(icons: List<Pair<CharSequence, Drawable>>) {
+ uiExecutor.execute {
+ val viewGroup = parent.requireViewById(R.id.controls_icon_row) as ViewGroup
+ viewGroup.removeAllViews()
+
+ val inflater = LayoutInflater.from(context)
+ icons.forEach {
+ val imageView = inflater.inflate(R.layout.controls_icon, viewGroup, false)
+ as ImageView
+ imageView.setContentDescription(it.first)
+ imageView.setImageDrawable(it.second)
+ viewGroup.addView(imageView)
+ }
+ }
}
private fun launchSelectorActivityListener(context: Context): (View) -> Unit {
@@ -206,6 +248,7 @@ class ControlsUiControllerImpl @Inject constructor (
parent.removeAllViews()
controlsById.clear()
controlViewsById.clear()
+ controlsListingController.get().removeCallback(listingCallback)
}
override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
@@ -231,9 +274,9 @@ class ControlsUiControllerImpl @Inject constructor (
}
}
- private fun createRow(inflater: LayoutInflater, parent: ViewGroup): ViewGroup {
- val row = inflater.inflate(R.layout.controls_row, parent, false) as ViewGroup
- parent.addView(row)
+ private fun createRow(inflater: LayoutInflater, listView: ViewGroup): ViewGroup {
+ val row = inflater.inflate(R.layout.controls_row, listView, false) as ViewGroup
+ listView.addView(row)
return row
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 14eec59211bd..9da99c453022 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -151,7 +151,7 @@ public class KeyguardViewMediator extends SystemUI {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
- private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ private static final boolean DEBUG = true;
private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
private final static String TAG = "KeyguardViewMediator";
diff --git a/packages/SystemUI/src/com/android/systemui/log/Event.java b/packages/SystemUI/src/com/android/systemui/log/Event.java
deleted file mode 100644
index 7bc1abfbb0d8..000000000000
--- a/packages/SystemUI/src/com/android/systemui/log/Event.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.log;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Stores information about an event that occurred in SystemUI to be used for debugging and triage.
- * Every event has a time stamp, log level and message.
- * Events are stored in {@link SysuiLog} and can be printed in a dumpsys.
- */
-public class Event {
- public static final int UNINITIALIZED = -1;
-
- @IntDef({ERROR, WARN, INFO, DEBUG, VERBOSE})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Level {}
- public static final int VERBOSE = 2;
- public static final int DEBUG = 3;
- public static final int INFO = 4;
- public static final int WARN = 5;
- public static final int ERROR = 6;
- public static final @Level int DEFAULT_LOG_LEVEL = DEBUG;
-
- private long mTimestamp;
- private @Level int mLogLevel = DEFAULT_LOG_LEVEL;
- private String mMessage = "";
-
- /**
- * initialize an event with a message
- */
- public Event init(String message) {
- init(DEFAULT_LOG_LEVEL, message);
- return this;
- }
-
- /**
- * initialize an event with a logLevel and message
- */
- public Event init(@Level int logLevel, String message) {
- mTimestamp = System.currentTimeMillis();
- mLogLevel = logLevel;
- mMessage = message;
- return this;
- }
-
- public String getMessage() {
- return mMessage;
- }
-
- public long getTimestamp() {
- return mTimestamp;
- }
-
- public @Level int getLogLevel() {
- return mLogLevel;
- }
-
- /**
- * Recycle this event
- */
- void recycle() {
- mTimestamp = -1;
- mLogLevel = DEFAULT_LOG_LEVEL;
- mMessage = "";
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
deleted file mode 100644
index 470f2b0d1b98..000000000000
--- a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.log;
-
-/**
- * Stores information about an event that occurred in SystemUI to be used for debugging and triage.
- * Every rich event has a time stamp, event type, and log level, with the option to provide the
- * reason this event was triggered.
- * Events are stored in {@link SysuiLog} and can be printed in a dumpsys.
- */
-public abstract class RichEvent extends Event {
- private int mType;
-
- /**
- * Initializes a rich event that includes an event type that matches with an index in the array
- * getEventLabels().
- */
- public RichEvent init(@Event.Level int logLevel, int type, String reason) {
- final int numEvents = getEventLabels().length;
- if (type < 0 || type >= numEvents) {
- throw new IllegalArgumentException("Unsupported event type. Events only supported"
- + " from 0 to " + (numEvents - 1) + ", but given type=" + type);
- }
- mType = type;
- super.init(logLevel, getEventLabels()[mType] + " " + reason);
- return this;
- }
-
- /**
- * Returns an array of the event labels. The index represents the event type and the
- * corresponding String stored at that index is the user-readable representation of that event.
- * @return array of user readable events, where the index represents its event type constant
- */
- public abstract String[] getEventLabels();
-
- @Override
- public void recycle() {
- super.recycle();
- mType = -1;
- }
-
- public int getType() {
- return mType;
- }
-
- /**
- * Builder to build a RichEvent.
- * @param <B> Log specific builder that is extending this builder
- * @param <E> Type of event we'll be building
- */
- public abstract static class Builder<B extends Builder<B, E>, E extends RichEvent> {
- public static final int UNINITIALIZED = -1;
-
- public final SysuiLog mLog;
- private B mBuilder = getBuilder();
- protected int mType;
- protected String mReason;
- protected @Level int mLogLevel;
-
- public Builder(SysuiLog sysuiLog) {
- mLog = sysuiLog;
- reset();
- }
-
- /**
- * Reset this builder's parameters so it can be reused to build another RichEvent.
- */
- public void reset() {
- mType = UNINITIALIZED;
- mReason = null;
- mLogLevel = VERBOSE;
- }
-
- /**
- * Get the log-specific builder.
- */
- public abstract B getBuilder();
-
- /**
- * Build the log-specific event given an event to populate.
- */
- public abstract E build(E e);
-
- /**
- * Optional - set the log level. Defaults to DEBUG.
- */
- public B setLogLevel(@Level int logLevel) {
- mLogLevel = logLevel;
- return mBuilder;
- }
-
- /**
- * Required - set the event type. These events must correspond with the events from
- * getEventLabels().
- */
- public B setType(int type) {
- mType = type;
- return mBuilder;
- }
-
- /**
- * Optional - set the reason why this event was triggered.
- */
- public B setReason(String reason) {
- mReason = reason;
- return mBuilder;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
deleted file mode 100644
index 9ee3e6765e4a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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.log;
-
-import android.os.Build;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.DumpController;
-import com.android.systemui.Dumpable;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayDeque;
-import java.util.Locale;
-
-/**
- * Thread-safe logger in SystemUI which prints logs to logcat and stores logs to be
- * printed by the DumpController. This is an alternative to printing directly
- * to avoid logs being deleted by chatty. The number of logs retained is varied based on
- * whether the build is {@link Build.IS_DEBUGGABLE}.
- *
- * To manually view the logs via adb:
- * adb shell dumpsys activity service com.android.systemui/.SystemUIService \
- * dependency DumpController <SysuiLogId>
- *
- * Logs can be disabled by setting the following SystemProperty and then restarting the device:
- * adb shell setprop persist.sysui.log.enabled.<id> true/false && adb reboot
- *
- * @param <E> Type of event we'll be logging
- */
-public class SysuiLog<E extends Event> implements Dumpable {
- public static final SimpleDateFormat DATE_FORMAT =
- new SimpleDateFormat("MM-dd HH:mm:ss.S", Locale.US);
-
- protected final Object mDataLock = new Object();
- private final String mId;
- private final int mMaxLogs;
- protected boolean mEnabled;
- protected boolean mLogToLogcatEnabled;
-
- @VisibleForTesting protected ArrayDeque<E> mTimeline;
-
- /**
- * Creates a SysuiLog
- * @param dumpController where to register this logger's dumpsys
- * @param id user-readable tag for this logger
- * @param maxDebugLogs maximum number of logs to retain when {@link sDebuggable} is true
- * @param maxLogs maximum number of logs to retain when {@link sDebuggable} is false
- */
- public SysuiLog(DumpController dumpController, String id, int maxDebugLogs, int maxLogs) {
- this(dumpController, id, sDebuggable ? maxDebugLogs : maxLogs,
- SystemProperties.getBoolean(SYSPROP_ENABLED_PREFIX + id, DEFAULT_ENABLED),
- SystemProperties.getBoolean(SYSPROP_LOGCAT_ENABLED_PREFIX + id,
- DEFAULT_LOGCAT_ENABLED));
- }
-
- @VisibleForTesting
- protected SysuiLog(DumpController dumpController, String id, int maxLogs, boolean enabled,
- boolean logcatEnabled) {
- mId = id;
- mMaxLogs = maxLogs;
- mEnabled = enabled;
- mLogToLogcatEnabled = logcatEnabled;
- mTimeline = mEnabled ? new ArrayDeque<>(mMaxLogs) : null;
- dumpController.registerDumpable(mId, this);
- }
-
- /**
- * Logs an event to the timeline which can be printed by the dumpsys.
- * May also log to logcat if enabled.
- * @return the last event that was discarded from the Timeline (can be recycled)
- */
- public E log(E event) {
- if (!mEnabled) {
- return null;
- }
-
- E recycledEvent = null;
- synchronized (mDataLock) {
- if (mTimeline.size() >= mMaxLogs) {
- recycledEvent = mTimeline.removeFirst();
- }
-
- mTimeline.add(event);
- }
-
- if (mLogToLogcatEnabled) {
- final String strEvent = eventToString(event);
- switch (event.getLogLevel()) {
- case Event.VERBOSE:
- Log.v(mId, strEvent);
- break;
- case Event.DEBUG:
- Log.d(mId, strEvent);
- break;
- case Event.ERROR:
- Log.e(mId, strEvent);
- break;
- case Event.INFO:
- Log.i(mId, strEvent);
- break;
- case Event.WARN:
- Log.w(mId, strEvent);
- break;
- }
- }
-
- if (recycledEvent != null) {
- recycledEvent.recycle();
- }
-
- return recycledEvent;
- }
-
- /**
- * @return user-readable string of the given event with timestamp
- */
- private String eventToTimestampedString(Event event) {
- StringBuilder sb = new StringBuilder();
- sb.append(SysuiLog.DATE_FORMAT.format(event.getTimestamp()));
- sb.append(" ");
- sb.append(event.getMessage());
- return sb.toString();
- }
-
- /**
- * @return user-readable string of the given event without a timestamp
- */
- public String eventToString(Event event) {
- return event.getMessage();
- }
-
- @GuardedBy("mDataLock")
- private void dumpTimelineLocked(PrintWriter pw) {
- pw.println("\tTimeline:");
-
- for (Event event : mTimeline) {
- pw.println("\t" + eventToTimestampedString(event));
- }
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(mId + ":");
-
- if (mEnabled) {
- synchronized (mDataLock) {
- dumpTimelineLocked(pw);
- }
- } else {
- pw.print(" - Logging disabled.");
- }
- }
-
- private static boolean sDebuggable = Build.IS_DEBUGGABLE;
- private static final String SYSPROP_ENABLED_PREFIX = "persist.sysui.log.enabled.";
- private static final String SYSPROP_LOGCAT_ENABLED_PREFIX = "persist.sysui.log.enabled.logcat.";
- private static final boolean DEFAULT_ENABLED = sDebuggable;
- private static final boolean DEFAULT_LOGCAT_ENABLED = false;
- private static final int DEFAULT_MAX_DEBUG_LOGS = 100;
- private static final int DEFAULT_MAX_LOGS = 50;
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 411980b399bd..ae6162219afa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -83,7 +83,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
mTile = new Tile();
updateDefaultTileAndIcon();
mServiceManager = host.getTileServices().getTileWrapper(this);
- if (mServiceManager.isBooleanTile()) {
+ if (mServiceManager.isToggleableTile()) {
// Replace states with BooleanState
resetStates();
}
@@ -252,7 +252,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
@Override
public State newTileState() {
- if (mServiceManager != null && mServiceManager.isBooleanTile()) {
+ if (mServiceManager != null && mServiceManager.isToggleableTile()) {
return new BooleanState();
}
return new State();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index ad79cadcc12d..17b0251837e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -141,16 +141,16 @@ public class TileLifecycleManager extends BroadcastReceiver implements
/**
* Determines whether the associated TileService is a Boolean Tile.
*
- * @return true if {@link TileService#META_DATA_BOOLEAN_TILE} is set to {@code true} for this
+ * @return true if {@link TileService#META_DATA_TOGGLEABLE_TILE} is set to {@code true} for this
* tile
- * @see TileService#META_DATA_BOOLEAN_TILE
+ * @see TileService#META_DATA_TOGGLEABLE_TILE
*/
- public boolean isBooleanTile() {
+ public boolean isToggleableTile() {
try {
ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
return info.metaData != null
- && info.metaData.getBoolean(TileService.META_DATA_BOOLEAN_TILE, false);
+ && info.metaData.getBoolean(TileService.META_DATA_TOGGLEABLE_TILE, false);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 1902d655abc3..cfa8fb6373a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -124,8 +124,8 @@ public class TileServiceManager {
return mStateManager.isActiveTile();
}
- public boolean isBooleanTile() {
- return mStateManager.isBooleanTile();
+ public boolean isToggleableTile() {
+ return mStateManager.isToggleableTile();
}
public void setShowingDialog(boolean dialog) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 7ad07c266cc3..7d3d4061014b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -20,7 +20,6 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Resources;
import android.graphics.Color;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
@@ -46,6 +45,7 @@ import com.android.internal.widget.ViewClippingUtil;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
+import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -97,8 +97,6 @@ public class KeyguardIndicationController implements StateListener,
private final LockPatternUtils mLockPatternUtils;
private final DockManager mDockManager;
- private final int mSlowThreshold;
- private final int mFastThreshold;
private final LockIcon mLockIcon;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
@@ -178,10 +176,6 @@ public class KeyguardIndicationController implements StateListener,
mWakeLock = new SettableWakeLock(wakeLock, TAG);
mLockPatternUtils = lockPatternUtils;
- Resources res = context.getResources();
- mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
- mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
-
mUserManager = context.getSystemService(UserManager.class);
mBatteryInfo = iBatteryStats;
@@ -484,12 +478,12 @@ public class KeyguardIndicationController implements StateListener,
int chargingId;
if (mPowerPluggedInWired) {
switch (mChargingSpeed) {
- case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
+ case BatteryStatus.CHARGING_FAST:
chargingId = hasChargingTime
? R.string.keyguard_indication_charging_time_fast
: R.string.keyguard_plugged_in_charging_fast;
break;
- case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
+ case BatteryStatus.CHARGING_SLOWLY:
chargingId = hasChargingTime
? R.string.keyguard_indication_charging_time_slowly
: R.string.keyguard_plugged_in_charging_slowly;
@@ -620,7 +614,7 @@ public class KeyguardIndicationController implements StateListener,
public static final int HIDE_DELAY_MS = 5000;
@Override
- public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
+ public void onRefreshBatteryInfo(BatteryStatus status) {
boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
|| status.status == BatteryManager.BATTERY_STATUS_FULL;
boolean wasPluggedIn = mPowerPluggedIn;
@@ -628,7 +622,7 @@ public class KeyguardIndicationController implements StateListener,
mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
mPowerCharged = status.isCharged();
mChargingWattage = status.maxChargingWattage;
- mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
+ mChargingSpeed = status.getChargingSpeed(mContext);
mBatteryLevel = status.level;
try {
mChargingTimeRemaining = mPowerPluggedIn
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 61915ad92d87..916da6eca0c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -47,8 +47,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationRankin
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.logging.NotifEvent;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -129,6 +127,8 @@ public class NotificationEntryManager implements
private final Map<NotificationEntry, NotificationLifetimeExtender> mRetainedNotifications =
new ArrayMap<>();
+ private final NotificationEntryManagerLogger mLogger;
+
// Lazily retrieved dependencies
private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
@@ -143,7 +143,6 @@ public class NotificationEntryManager implements
private NotificationPresenter mPresenter;
private RankingMap mLatestRankingMap;
- private NotifLog mNotifLog;
@VisibleForTesting
final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
@@ -184,7 +183,7 @@ public class NotificationEntryManager implements
@Inject
public NotificationEntryManager(
- NotifLog notifLog,
+ NotificationEntryManagerLogger logger,
NotificationGroupManager groupManager,
NotificationRankingManager rankingManager,
KeyguardEnvironment keyguardEnvironment,
@@ -193,7 +192,7 @@ public class NotificationEntryManager implements
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
ForegroundServiceDismissalFeatureController fgsFeatureController) {
- mNotifLog = notifLog;
+ mLogger = logger;
mGroupManager = groupManager;
mRankingManager = rankingManager;
mKeyguardEnvironment = keyguardEnvironment;
@@ -291,13 +290,12 @@ public class NotificationEntryManager implements
NotificationEntry entry = mPendingNotifications.get(key);
entry.abortTask();
mPendingNotifications.remove(key);
- mNotifLog.log(NotifEvent.INFLATION_ABORTED, entry, "PendingNotification aborted"
- + " reason=" + reason);
+ mLogger.logInflationAborted(key, "pending", reason);
}
NotificationEntry addedEntry = getActiveNotificationUnfiltered(key);
if (addedEntry != null) {
addedEntry.abortTask();
- mNotifLog.log(NotifEvent.INFLATION_ABORTED, addedEntry.getKey() + " " + reason);
+ mLogger.logInflationAborted(key, "active", reason);
}
}
@@ -328,9 +326,9 @@ public class NotificationEntryManager implements
// the list, otherwise we might get leaks.
if (!entry.isRowRemoved()) {
boolean isNew = getActiveNotificationUnfiltered(entry.getKey()) == null;
+ mLogger.logNotifInflated(entry.getKey(), isNew);
if (isNew) {
for (NotificationEntryListener listener : mNotificationEntryListeners) {
- mNotifLog.log(NotifEvent.INFLATED, entry);
listener.onEntryInflated(entry);
}
addActiveNotification(entry);
@@ -340,7 +338,6 @@ public class NotificationEntryManager implements
}
} else {
for (NotificationEntryListener listener : mNotificationEntryListeners) {
- mNotifLog.log(NotifEvent.INFLATED, entry);
listener.onEntryReinflated(entry);
}
}
@@ -422,7 +419,7 @@ public class NotificationEntryManager implements
for (NotificationRemoveInterceptor interceptor : mRemoveInterceptors) {
if (interceptor.onNotificationRemoveRequested(key, entry, reason)) {
// Remove intercepted; log and skip
- mNotifLog.log(NotifEvent.REMOVE_INTERCEPTED);
+ mLogger.logRemovalIntercepted(key);
return;
}
}
@@ -437,10 +434,7 @@ public class NotificationEntryManager implements
if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) {
extendLifetime(pendingEntry, extender);
lifetimeExtended = true;
- mNotifLog.log(
- NotifEvent.LIFETIME_EXTENDED,
- pendingEntry.getSbn(),
- "pendingEntry extendedBy=" + extender.toString());
+ mLogger.logLifetimeExtended(key, extender.getClass().getName(), "pending");
}
}
}
@@ -460,10 +454,7 @@ public class NotificationEntryManager implements
mLatestRankingMap = ranking;
extendLifetime(entry, extender);
lifetimeExtended = true;
- mNotifLog.log(
- NotifEvent.LIFETIME_EXTENDED,
- entry.getSbn(),
- "entry extendedBy=" + extender.toString());
+ mLogger.logLifetimeExtended(key, extender.getClass().getName(), "active");
break;
}
}
@@ -486,8 +477,7 @@ public class NotificationEntryManager implements
mLeakDetector.trackGarbage(entry);
removedByUser |= entryDismissed;
- mNotifLog.log(NotifEvent.NOTIF_REMOVED, entry.getSbn(),
- "removedByUser=" + removedByUser);
+ mLogger.logNotifRemoved(entry.getKey(), removedByUser);
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onEntryRemoved(entry, visibility, removedByUser);
}
@@ -576,7 +566,7 @@ public class NotificationEntryManager implements
abortExistingInflation(key, "addNotification");
mPendingNotifications.put(key, entry);
- mNotifLog.log(NotifEvent.NOTIF_ADDED, entry);
+ mLogger.logNotifAdded(entry.getKey());
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onPendingEntryAdded(entry);
}
@@ -613,7 +603,7 @@ public class NotificationEntryManager implements
entry.setSbn(notification);
mGroupManager.onEntryUpdated(entry, oldSbn);
- mNotifLog.log(NotifEvent.NOTIF_UPDATED, entry);
+ mLogger.logNotifUpdated(entry.getKey());
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onPreEntryUpdated(entry);
}
@@ -808,7 +798,7 @@ public class NotificationEntryManager implements
//TODO: Get rid of this in favor of NotificationUpdateHandler#updateNotificationRanking
/**
* @param rankingMap the {@link RankingMap} to apply to the current notification list
- * @param reason the reason for calling this method, for {@link NotifLog}
+ * @param reason the reason for calling this method, which will be logged
*/
public void updateRanking(RankingMap rankingMap, String reason) {
updateRankingAndSort(rankingMap, reason);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
new file mode 100644
index 000000000000..4382ab50390a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+/** Logger for [NotificationEntryManager]. */
+class NotificationEntryManagerLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+ fun logNotifAdded(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ }, {
+ "NOTIF ADDED $str1"
+ })
+ }
+
+ fun logNotifUpdated(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ }, {
+ "NOTIF UPDATED $str1"
+ })
+ }
+
+ fun logInflationAborted(key: String, status: String, reason: String) {
+ buffer.log(TAG, DEBUG, {
+ str1 = key
+ str2 = status
+ str3 = reason
+ }, {
+ "NOTIF INFLATION ABORTED $str1 notifStatus=$str2 reason=$str3"
+ })
+ }
+
+ fun logNotifInflated(key: String, isNew: Boolean) {
+ buffer.log(TAG, DEBUG, {
+ str1 = key
+ bool1 = isNew
+ }, {
+ "NOTIF INFLATED $str1 isNew=$bool1}"
+ })
+ }
+
+ fun logRemovalIntercepted(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ }, {
+ "NOTIF REMOVE INTERCEPTED for $str1"
+ })
+ }
+
+ fun logLifetimeExtended(key: String, extenderName: String, status: String) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ str2 = extenderName
+ str3 = status
+ }, {
+ "NOTIF LIFETIME EXTENDED $str1 extender=$str2 status=$str3"
+ })
+ }
+
+ fun logNotifRemoved(key: String, removedByUser: Boolean) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ bool1 = removedByUser
+ }, {
+ "NOTIF REMOVED $str1 removedByUser=$bool1"
+ })
+ }
+
+ fun logFilterAndSort(reason: String) {
+ buffer.log(TAG, INFO, {
+ str1 = reason
+ }, {
+ "FILTER AND SORT reason=$str1"
+ })
+ }
+}
+
+private const val TAG = "NotificationEntryMgr" \ No newline at end of file
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 7fe229c26f3a..3fa1954a7fcc 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
@@ -123,6 +123,16 @@ public class ListDumper {
.append(" ");
}
+ if (!notifEntry.mDismissInterceptors.isEmpty()) {
+ String[] interceptorsNames = new String[notifEntry.mDismissInterceptors.size()];
+ for (int i = 0; i < interceptorsNames.length; i++) {
+ interceptorsNames[i] = notifEntry.mDismissInterceptors.get(i).getName();
+ }
+ rksb.append("dismissInterceptors=")
+ .append(Arrays.toString(interceptorsNames))
+ .append(" ");
+ }
+
if (notifEntry.mExcludingFilter != null) {
rksb.append("filter=")
.append(notifEntry.mExcludingFilter)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 3b2fe9441c32..38d8d979a4da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -63,6 +63,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.Assert;
@@ -116,6 +117,7 @@ public class NotifCollection implements Dumpable {
@Nullable private CollectionReadyForBuildListener mBuildListener;
private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
private final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
+ private final List<NotifDismissInterceptor> mDismissInterceptors = new ArrayList<>();
private boolean mAttached = false;
private boolean mAmDispatchingToOtherCode;
@@ -176,10 +178,21 @@ public class NotifCollection implements Dumpable {
extender.setCallback(this::onEndLifetimeExtension);
}
+ /** @see NotifPipeline#addNotificationDismissInterceptor(NotifDismissInterceptor) */
+ void addNotificationDismissInterceptor(NotifDismissInterceptor interceptor) {
+ Assert.isMainThread();
+ checkForReentrantCall();
+ if (mDismissInterceptors.contains(interceptor)) {
+ throw new IllegalArgumentException("Interceptor " + interceptor + " already added.");
+ }
+ mDismissInterceptors.add(interceptor);
+ interceptor.setCallback(this::onEndDismissInterception);
+ }
+
/**
* Dismiss a notification on behalf of the user.
*/
- void dismissNotification(NotificationEntry entry, @NonNull DismissedByUserStats stats) {
+ public void dismissNotification(NotificationEntry entry, @NonNull DismissedByUserStats stats) {
Assert.isMainThread();
requireNonNull(stats);
checkForReentrantCall();
@@ -192,6 +205,12 @@ public class NotifCollection implements Dumpable {
return;
}
+ updateDismissInterceptors(entry);
+ if (isDismissIntercepted(entry)) {
+ mLogger.logNotifDismissedIntercepted(entry.getKey());
+ return;
+ }
+
// Optimistically mark the notification as dismissed -- we'll wait for the signal from
// system server before removing it from our notification set.
entry.setDismissState(DISMISSED);
@@ -236,7 +255,6 @@ public class NotifCollection implements Dumpable {
for (NotificationEntry canceledEntry : canceledEntries) {
tryRemoveNotification(canceledEntry);
}
-
rebuildList();
}
@@ -307,11 +325,11 @@ public class NotifCollection implements Dumpable {
// Update to an existing entry
mLogger.logNotifUpdated(sbn.getKey());
+ // Notification is updated so it is essentially re-added and thus alive again, so we
+ // can reset its state.
cancelLocalDismissal(entry);
-
- // Notification is updated so it is essentially re-added and thus alive again. Don't
- // need to keep its lifetime extended.
cancelLifetimeExtension(entry);
+ cancelDismissInterception(entry);
entry.mCancellationReason = REASON_NOT_CANCELED;
entry.setSbn(sbn);
@@ -348,6 +366,7 @@ public class NotifCollection implements Dumpable {
if (!isLifetimeExtended(entry)) {
mNotificationSet.remove(entry.getKey());
+ cancelDismissInterception(entry);
dispatchOnEntryRemoved(entry, entry.mCancellationReason);
dispatchOnEntryCleanUp(entry);
return true;
@@ -436,6 +455,17 @@ public class NotifCollection implements Dumpable {
mAmDispatchingToOtherCode = false;
}
+ private void updateDismissInterceptors(@NonNull NotificationEntry entry) {
+ entry.mDismissInterceptors.clear();
+ mAmDispatchingToOtherCode = true;
+ for (NotifDismissInterceptor interceptor : mDismissInterceptors) {
+ if (interceptor.shouldInterceptDismissal(entry)) {
+ entry.mDismissInterceptors.add(interceptor);
+ }
+ }
+ mAmDispatchingToOtherCode = false;
+ }
+
private void cancelLocalDismissal(NotificationEntry entry) {
if (isDismissedByUser(entry)) {
entry.setDismissState(NOT_DISMISSED);
@@ -450,6 +480,42 @@ public class NotifCollection implements Dumpable {
}
}
+ private void onEndDismissInterception(
+ NotifDismissInterceptor interceptor,
+ NotificationEntry entry,
+ @NonNull DismissedByUserStats stats) {
+ Assert.isMainThread();
+ if (!mAttached) {
+ return;
+ }
+ checkForReentrantCall();
+
+ if (!entry.mDismissInterceptors.remove(interceptor)) {
+ throw new IllegalStateException(
+ String.format(
+ "Cannot end dismiss interceptor for interceptor \"%s\" (%s)",
+ interceptor.getName(),
+ interceptor));
+ }
+
+ if (!isDismissIntercepted(entry)) {
+ dismissNotification(entry, stats);
+ }
+ }
+
+ private void cancelDismissInterception(NotificationEntry entry) {
+ mAmDispatchingToOtherCode = true;
+ for (NotifDismissInterceptor interceptor : entry.mDismissInterceptors) {
+ interceptor.cancelDismissInterception(entry);
+ }
+ mAmDispatchingToOtherCode = false;
+ entry.mDismissInterceptors.clear();
+ }
+
+ private boolean isDismissIntercepted(NotificationEntry entry) {
+ return entry.mDismissInterceptors.size() > 0;
+ }
+
private void checkForReentrantCall() {
if (mAmDispatchingToOtherCode) {
throw new IllegalStateException("Reentrant call detected");
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 5767ad93014e..d4d2369ba822 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
@@ -25,6 +25,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import java.util.Collection;
@@ -97,13 +98,21 @@ public class NotifPipeline implements CommonNotifCollection {
/**
* Registers a lifetime extender. Lifetime extenders can cause notifications that have been
- * dismissed or retracted to be temporarily retained in the collection.
+ * dismissed or retracted by system server to be temporarily retained in the collection.
*/
public void addNotificationLifetimeExtender(NotifLifetimeExtender extender) {
mNotifCollection.addNotificationLifetimeExtender(extender);
}
/**
+ * Registers a dismiss interceptor. Dismiss interceptors can cause notifications that have been
+ * dismissed by the user to be retained (won't send a dismissal to system server).
+ */
+ public void addNotificationDismissInterceptor(NotifDismissInterceptor interceptor) {
+ mNotifCollection.addNotificationDismissInterceptor(interceptor);
+ }
+
+ /**
* Registers a filter with the pipeline before grouping, promoting and sorting occurs. Filters
* are called on each notification in the order that they were registered. If any filter
* returns true, the notification is removed from the pipeline (and no other filters are
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 5dbf47e29407..41c1b7b5fae8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -66,6 +66,7 @@ import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
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.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -104,6 +105,9 @@ public final class NotificationEntry extends ListEntry {
/** List of lifetime extenders that are extending the lifetime of this notification. */
final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
+ /** List of dismiss interceptors that are intercepting the dismissal of this notification. */
+ final List<NotifDismissInterceptor> mDismissInterceptors = new ArrayList<>();
+
/** If this notification was filtered out, then the filter that did the filtering. */
@Nullable NotifFilter mExcludingFilter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 1eeeab3e93cb..2981252f148c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -22,11 +22,10 @@ import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.NotificationListenerService.RankingMap
import android.service.notification.StatusBarNotification
import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
-import com.android.systemui.statusbar.notification.logging.NotifEvent
-import com.android.systemui.statusbar.notification.logging.NotifLog
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
@@ -53,7 +52,7 @@ open class NotificationRankingManager @Inject constructor(
private val groupManager: NotificationGroupManager,
private val headsUpManager: HeadsUpManager,
private val notifFilter: NotificationFilter,
- private val notifLog: NotifLog,
+ private val logger: NotificationEntryManagerLogger,
sectionsFeatureManager: NotificationSectionsFeatureManager,
private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
private val highPriorityProvider: HighPriorityProvider
@@ -134,7 +133,7 @@ open class NotificationRankingManager @Inject constructor(
entries: Sequence<NotificationEntry>,
reason: String
): Sequence<NotificationEntry> {
- notifLog.log(NotifEvent.FILTER_AND_SORT, reason)
+ logger.logFilterAndSort(reason)
return entries.filter { !notifFilter.shouldFilterOut(it) }
.sortedWith(rankingComparator)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
new file mode 100644
index 000000000000..116c70c4f1cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static android.service.notification.NotificationStats.DISMISSAL_OTHER;
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_UNKNOWN;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+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.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Coordinates hiding, intercepting (the dismissal), and deletion of bubbled notifications.
+ *
+ * The typical "start state" for a bubbled notification is when a bubble-able notification is
+ * posted. It is visible as a bubble AND as a notification in the shade. From here, we can get
+ * into a few hidden-from-shade states described below:
+ *
+ * Start State -> Hidden from shade
+ * User expands the bubble so we hide its notification from the shade.
+ * OR
+ * User dismisses a group summary with a bubbled child. All bubbled children are now hidden from
+ * the shade. And the group summary's dismissal is intercepted + hidden from the shade (see below).
+ *
+ * Start State -> Dismissal intercepted + hidden from shade
+ * User dismisses the notification from the shade. We now hide the notification from the shade
+ * and intercept its dismissal (the removal signal is never sent to system server). We
+ * keep the notification alive in system server so that {@link BubbleController} can still
+ * respond to app-cancellations (ie: remove the bubble if the app cancels the notification).
+ *
+ */
+@Singleton
+public class BubbleCoordinator implements Coordinator {
+ private static final String TAG = "BubbleCoordinator";
+
+ private final BubbleController mBubbleController;
+ private final NotifCollection mNotifCollection;
+ private final Set<String> mInterceptedDismissalEntries = new HashSet<>();
+ private NotifPipeline mNotifPipeline;
+ private NotifDismissInterceptor.OnEndDismissInterception mOnEndDismissInterception;
+
+ @Inject
+ public BubbleCoordinator(
+ BubbleController bubbleController,
+ NotifCollection notifCollection) {
+ mBubbleController = bubbleController;
+ mNotifCollection = notifCollection;
+ }
+
+ @Override
+ public void attach(NotifPipeline pipeline) {
+ mNotifPipeline = pipeline;
+ mNotifPipeline.addNotificationDismissInterceptor(mDismissInterceptor);
+ mNotifPipeline.addPreRenderFilter(mNotifFilter);
+ mBubbleController.addNotifCallback(mNotifCallback);
+ }
+
+ private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ return mBubbleController.isBubbleNotificationSuppressedFromShade(entry);
+ }
+ };
+
+ private final NotifDismissInterceptor mDismissInterceptor = new NotifDismissInterceptor() {
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
+ @Override
+ public void setCallback(OnEndDismissInterception callback) {
+ mOnEndDismissInterception = callback;
+ }
+
+ @Override
+ public boolean shouldInterceptDismissal(NotificationEntry entry) {
+ // TODO: b/149041810 add support for intercepting app-cancelled bubble notifications
+ // for experimental bubbles
+ if (mBubbleController.handleDismissalInterception(entry)) {
+ mInterceptedDismissalEntries.add(entry.getKey());
+ return true;
+ } else {
+ mInterceptedDismissalEntries.remove(entry.getKey());
+ return false;
+ }
+ }
+
+ @Override
+ public void cancelDismissInterception(NotificationEntry entry) {
+ mInterceptedDismissalEntries.remove(entry.getKey());
+ }
+ };
+
+ private final BubbleController.NotifCallback mNotifCallback =
+ new BubbleController.NotifCallback() {
+ @Override
+ public void removeNotification(NotificationEntry entry, int reason) {
+ if (isInterceptingDismissal(entry)) {
+ mInterceptedDismissalEntries.remove(entry.getKey());
+ mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
+ createDismissedByUserStats(entry));
+ } else if (mNotifPipeline.getActiveNotifs().contains(entry)) {
+ // Bubbles are hiding the notifications from the shade, but the bubble was
+ // deleted; therefore, the notification should be cancelled as if it were a user
+ // dismissal (this won't re-enter handleInterceptDimissal because Bubbles
+ // will have already marked it as no longer a bubble)
+ mNotifCollection.dismissNotification(entry, createDismissedByUserStats(entry));
+ }
+ }
+
+ @Override
+ public void invalidateNotifications(String reason) {
+ mNotifFilter.invalidateList();
+ }
+
+ @Override
+ public void maybeCancelSummary(NotificationEntry entry) {
+ // no-op
+ }
+ };
+
+ private boolean isInterceptingDismissal(NotificationEntry entry) {
+ return mInterceptedDismissalEntries.contains(entry.getKey());
+ }
+
+ private DismissedByUserStats createDismissedByUserStats(NotificationEntry entry) {
+ return new DismissedByUserStats(
+ DISMISSAL_OTHER,
+ DISMISS_SENTIMENT_UNKNOWN,
+ NotificationVisibility.obtain(entry.getKey(),
+ entry.getRanking().getRank(),
+ mNotifPipeline.getActiveNotifs().size(),
+ true, // was visible as a bubble
+ NotificationLogger.getNotificationLocation(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 0a1e09f4c99d..7a9547c573bb 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
@@ -53,6 +53,7 @@ public class NotifCoordinators implements Dumpable {
RankingCoordinator rankingCoordinator,
ForegroundCoordinator foregroundCoordinator,
DeviceProvisionedCoordinator deviceProvisionedCoordinator,
+ BubbleCoordinator bubbleCoordinator,
PreparationCoordinator preparationCoordinator) {
dumpController.registerDumpable(TAG, this);
@@ -61,6 +62,7 @@ public class NotifCoordinators implements Dumpable {
mCoordinators.add(rankingCoordinator);
mCoordinators.add(foregroundCoordinator);
mCoordinators.add(deviceProvisionedCoordinator);
+ mCoordinators.add(bubbleCoordinator);
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
mCoordinators.add(preparationCoordinator);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 41314b86695a..1e5946a85cfa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -22,8 +22,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.logging.NotifEvent;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import java.util.ArrayList;
import java.util.List;
@@ -42,13 +40,15 @@ import javax.inject.Singleton;
public class PreparationCoordinator implements Coordinator {
private static final String TAG = "PreparationCoordinator";
- private final NotifLog mNotifLog;
+ private final PreparationCoordinatorLogger mLogger;
private final NotifInflater mNotifInflater;
private final List<NotificationEntry> mPendingNotifications = new ArrayList<>();
@Inject
- public PreparationCoordinator(NotifLog notifLog, NotifInflaterImpl notifInflater) {
- mNotifLog = notifLog;
+ public PreparationCoordinator(
+ PreparationCoordinatorLogger logger,
+ NotifInflaterImpl notifInflater) {
+ mLogger = logger;
mNotifInflater = notifInflater;
mNotifInflater.setInflationCallback(mInflationCallback);
}
@@ -106,7 +106,7 @@ public class PreparationCoordinator implements Coordinator {
new NotifInflater.InflationCallback() {
@Override
public void onInflationFinished(NotificationEntry entry) {
- mNotifLog.log(NotifEvent.INFLATED, entry);
+ mLogger.logNotifInflated(entry.getKey());
mPendingNotifications.remove(entry);
mNotifInflatingFilter.invalidateList();
}
@@ -123,7 +123,7 @@ public class PreparationCoordinator implements Coordinator {
}
private void abortInflation(NotificationEntry entry, String reason) {
- mNotifLog.log(NotifEvent.INFLATION_ABORTED, reason);
+ mLogger.logInflationAborted(entry.getKey(), reason);
entry.abortTask();
mPendingNotifications.remove(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
new file mode 100644
index 000000000000..75e7bc9b79a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+class PreparationCoordinatorLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+ fun logNotifInflated(key: String) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = key
+ }, {
+ "NOTIF INFLATED $str1"
+ })
+ }
+
+ fun logInflationAborted(key: String, reason: String) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = key
+ str2 = reason
+ }, {
+ "NOTIF INFLATION ABORTED $str1 reason=$str2"
+ })
+ }
+}
+
+private const val TAG = "PreparationCoordinator" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 59d82a1bc5cf..ecf62db4680b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -295,6 +295,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
}
//TODO: Replace this API with RowContentBindParams directly
row.setNeedsRedaction(mNotificationLockscreenUserManager.needsRedaction(entry));
+ params.rebindAllContentViews();
mRowContentBindStage.requestRebind(entry, en -> {
row.setUsesIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
row.setUsesIncreasedHeadsUpHeight(useIncreasedHeadsUp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index 14e15031056f..dc7a50d621a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -69,6 +69,14 @@ class NotifCollectionLogger @Inject constructor(
})
}
+ fun logNotifDismissedIntercepted(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ }, {
+ "DISMISS INTERCEPTED $str1"
+ })
+ }
+
fun logRankingMissing(key: String, rankingMap: RankingMap) {
buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" })
buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifDismissInterceptor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifDismissInterceptor.java
new file mode 100644
index 000000000000..3354ad1daf20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifDismissInterceptor.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.notifcollection;
+
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * A way for coordinators to temporarily intercept a user-dismissed notification before a message
+ * is sent to system server to officially remove this notification.
+ * See {@link NotifCollection#addNotificationDismissInterceptor(NotifDismissInterceptor)}.
+ */
+public interface NotifDismissInterceptor {
+ /** Name to associate with this interceptor (for the purposes of debugging) */
+ String getName();
+
+ /**
+ * Called on the interceptor immediately after it has been registered. The interceptor should
+ * hang on to this callback and execute it whenever it no longer needs to intercept the
+ * dismissal of the notification.
+ */
+ void setCallback(OnEndDismissInterception callback);
+
+ /**
+ * Called by the NotifCollection whenever a notification has been dismissed (by the user).
+ * If the interceptor returns true, it is considered to be intercepting the notification.
+ * Intercepted notifications will not be sent to system server for removal until it is no
+ * longer being intercepted. However, the notification can still be cancelled by the app.
+ * This method is called on all interceptors even if earlier ones return true.
+ */
+ boolean shouldInterceptDismissal(NotificationEntry entry);
+
+
+ /**
+ * Called by the NotifCollection to inform a DismissInterceptor that its interception of a notif
+ * is no longer valid (usually because the notif has been removed by means other than the
+ * user dismissing the notification from the shade, or the notification has been updated). The
+ * interceptor should clean up any references it has to the notif in question.
+ */
+ void cancelDismissInterception(NotificationEntry entry);
+
+ /**
+ * Callback for notifying the NotifCollection that it no longer is intercepting the dismissal.
+ * If the end of this dismiss interception triggers a dismiss (ie: no other
+ * NotifDismissInterceptors are intercepting the entry), NotifCollection will use stats
+ * in the message sent to system server for the notification's dismissal.
+ */
+ interface OnEndDismissInterception {
+ void onEndDismissInterception(
+ NotifDismissInterceptor interceptor,
+ NotificationEntry entry,
+ DismissedByUserStats stats);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
deleted file mode 100644
index 9adceb78c249..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * 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.statusbar.notification.logging;
-
-import android.annotation.IntDef;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-
-import com.android.systemui.log.RichEvent;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
-import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * An event related to notifications. {@link NotifLog} stores and prints these events for debugging
- * and triaging purposes. We do not store a copy of the status bar notification nor ranking
- * here to mitigate memory usage.
- */
-public class NotifEvent extends RichEvent {
- /**
- * Initializes a rich event that includes an event type that matches with an index in the array
- * getEventLabels().
- */
- public NotifEvent init(@EventType int type, StatusBarNotification sbn,
- NotificationListenerService.Ranking ranking, String reason) {
- StringBuilder extraInfo = new StringBuilder(reason);
- if (sbn != null) {
- extraInfo.append(" " + sbn.getKey());
- }
-
- if (ranking != null) {
- extraInfo.append(" Ranking=");
- extraInfo.append(ranking.getRank());
- }
- super.init(INFO, type, extraInfo.toString());
- return this;
- }
-
- /**
- * Event labels for ListBuilderEvents
- * Index corresponds to an # in {@link EventType}
- */
- @Override
- public String[] getEventLabels() {
- assert (TOTAL_EVENT_LABELS
- == (TOTAL_NEM_EVENT_TYPES
- + TOTAL_LIST_BUILDER_EVENT_TYPES
- + TOTAL_COALESCER_EVENT_TYPES));
- return EVENT_LABELS;
- }
-
- /**
- * @return if this event occurred in {@link ShadeListBuilder}
- */
- static boolean isListBuilderEvent(@EventType int type) {
- return isBetweenInclusive(type, 0, TOTAL_LIST_BUILDER_EVENT_TYPES);
- }
-
- /**
- * @return if this event occurred in {@link NotificationEntryManager}
- */
- static boolean isNemEvent(@EventType int type) {
- return isBetweenInclusive(type, TOTAL_LIST_BUILDER_EVENT_TYPES,
- TOTAL_LIST_BUILDER_EVENT_TYPES + TOTAL_NEM_EVENT_TYPES);
- }
-
- private static boolean isBetweenInclusive(int x, int a, int b) {
- return x >= a && x <= b;
- }
-
- @IntDef({
- // NotifListBuilder events:
- WARN,
- ON_BUILD_LIST,
- START_BUILD_LIST,
- DISPATCH_FINAL_LIST,
- LIST_BUILD_COMPLETE,
- PRE_GROUP_FILTER_INVALIDATED,
- PROMOTER_INVALIDATED,
- SECTION_INVALIDATED,
- COMPARATOR_INVALIDATED,
- PARENT_CHANGED,
- FILTER_CHANGED,
- PROMOTER_CHANGED,
- PRE_RENDER_FILTER_INVALIDATED,
-
- // NotificationEntryManager events:
- NOTIF_ADDED,
- NOTIF_REMOVED,
- NOTIF_UPDATED,
- FILTER,
- SORT,
- FILTER_AND_SORT,
- NOTIF_VISIBILITY_CHANGED,
- LIFETIME_EXTENDED,
- REMOVE_INTERCEPTED,
- INFLATION_ABORTED,
- INFLATED,
-
- // GroupCoalescer
- COALESCED_EVENT,
- EARLY_BATCH_EMIT,
- EMIT_EVENT_BATCH
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface EventType {}
-
- private static final String[] EVENT_LABELS =
- new String[]{
- // NotifListBuilder labels:
- "Warning",
- "OnBuildList",
- "StartBuildList",
- "DispatchFinalList",
- "ListBuildComplete",
- "FilterInvalidated",
- "PromoterInvalidated",
- "SectionInvalidated",
- "ComparatorInvalidated",
- "ParentChanged",
- "FilterChanged",
- "PromoterChanged",
- "FinalFilterInvalidated",
- "SectionerChanged",
-
- // NEM event labels:
- "NotifAdded",
- "NotifRemoved",
- "NotifUpdated",
- "Filter",
- "Sort",
- "FilterAndSort",
- "NotifVisibilityChanged",
- "LifetimeExtended",
- "RemoveIntercepted",
- "InflationAborted",
- "Inflated",
-
- // GroupCoalescer labels:
- "CoalescedEvent",
- "EarlyBatchEmit",
- "EmitEventBatch",
- "BatchMaxTimeout"
- };
-
- private static final int TOTAL_EVENT_LABELS = EVENT_LABELS.length;
-
- /**
- * Events related to {@link ShadeListBuilder}
- */
- public static final int WARN = 0;
- public static final int ON_BUILD_LIST = 1;
- public static final int START_BUILD_LIST = 2;
- public static final int DISPATCH_FINAL_LIST = 3;
- public static final int LIST_BUILD_COMPLETE = 4;
- public static final int PRE_GROUP_FILTER_INVALIDATED = 5;
- public static final int PROMOTER_INVALIDATED = 6;
- public static final int SECTION_INVALIDATED = 7;
- public static final int COMPARATOR_INVALIDATED = 8;
- public static final int PARENT_CHANGED = 9;
- public static final int FILTER_CHANGED = 10;
- public static final int PROMOTER_CHANGED = 11;
- public static final int PRE_RENDER_FILTER_INVALIDATED = 12;
- public static final int SECTION_CHANGED = 13;
- private static final int TOTAL_LIST_BUILDER_EVENT_TYPES = 14;
-
- /**
- * Events related to {@link NotificationEntryManager}
- */
- private static final int NEM_EVENT_START_INDEX = TOTAL_LIST_BUILDER_EVENT_TYPES;
- public static final int NOTIF_ADDED = NEM_EVENT_START_INDEX;
- public static final int NOTIF_REMOVED = NEM_EVENT_START_INDEX + 1;
- public static final int NOTIF_UPDATED = NEM_EVENT_START_INDEX + 2;
- public static final int FILTER = NEM_EVENT_START_INDEX + 3;
- public static final int SORT = NEM_EVENT_START_INDEX + 4;
- public static final int FILTER_AND_SORT = NEM_EVENT_START_INDEX + 5;
- public static final int NOTIF_VISIBILITY_CHANGED = NEM_EVENT_START_INDEX + 6;
- public static final int LIFETIME_EXTENDED = NEM_EVENT_START_INDEX + 7;
- // unable to remove notif - removal intercepted by {@link NotificationRemoveInterceptor}
- public static final int REMOVE_INTERCEPTED = NEM_EVENT_START_INDEX + 8;
- public static final int INFLATION_ABORTED = NEM_EVENT_START_INDEX + 9;
- public static final int INFLATED = NEM_EVENT_START_INDEX + 10;
- private static final int TOTAL_NEM_EVENT_TYPES = 11;
-
- /**
- * Events related to {@link GroupCoalescer}
- */
- private static final int COALESCER_EVENT_START_INDEX = NEM_EVENT_START_INDEX
- + TOTAL_NEM_EVENT_TYPES;
- public static final int COALESCED_EVENT = COALESCER_EVENT_START_INDEX;
- public static final int EARLY_BATCH_EMIT = COALESCER_EVENT_START_INDEX + 1;
- public static final int EMIT_EVENT_BATCH = COALESCER_EVENT_START_INDEX + 2;
- public static final int BATCH_MAX_TIMEOUT = COALESCER_EVENT_START_INDEX + 3;
- private static final int TOTAL_COALESCER_EVENT_TYPES = 3;
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
deleted file mode 100644
index 299d628d0fd2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.statusbar.notification.logging;
-
-import android.os.SystemProperties;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.StatusBarNotification;
-
-import com.android.systemui.DumpController;
-import com.android.systemui.log.SysuiLog;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * Logs systemui notification events for debugging and triaging purposes. Logs are dumped in
- * bugreports or on demand:
- * adb shell dumpsys activity service com.android.systemui/.SystemUIService \
- * dependency DumpController NotifLog
- */
-@Singleton
-public class NotifLog extends SysuiLog<NotifEvent> {
- private static final String TAG = "NotifLog";
- private static final boolean SHOW_NEM_LOGS =
- SystemProperties.getBoolean("persist.sysui.log.notif.nem", true);
- private static final boolean SHOW_LIST_BUILDER_LOGS =
- SystemProperties.getBoolean("persist.sysui.log.notif.listbuilder", true);
-
- private static final int MAX_DOZE_DEBUG_LOGS = 400;
- private static final int MAX_DOZE_LOGS = 50;
-
- private NotifEvent mRecycledEvent;
-
- @Inject
- public NotifLog(DumpController dumpController) {
- super(dumpController, TAG, MAX_DOZE_DEBUG_LOGS, MAX_DOZE_LOGS);
- }
-
- /**
- * Logs a {@link NotifEvent} with a notification, ranking and message.
- * Uses the last recycled event if available.
- * @return true if successfully logged, else false
- */
- public void log(@NotifEvent.EventType int eventType,
- StatusBarNotification sbn, Ranking ranking, String msg) {
- if (!mEnabled
- || (NotifEvent.isListBuilderEvent(eventType) && !SHOW_LIST_BUILDER_LOGS)
- || (NotifEvent.isNemEvent(eventType) && !SHOW_NEM_LOGS)) {
- return;
- }
-
- if (mRecycledEvent != null) {
- mRecycledEvent = log(mRecycledEvent.init(eventType, sbn, ranking, msg));
- } else {
- mRecycledEvent = log(new NotifEvent().init(eventType, sbn, ranking, msg));
- }
- }
-
- /**
- * Logs a {@link NotifEvent} with no extra information aside from the event type
- */
- public void log(@NotifEvent.EventType int eventType) {
- log(eventType, null, null, "");
- }
-
- /**
- * Logs a {@link NotifEvent} with a message
- */
- public void log(@NotifEvent.EventType int eventType, String msg) {
- log(eventType, null, null, msg);
- }
-
- /**
- * Logs a {@link NotifEvent} with a entry
- */
- public void log(@NotifEvent.EventType int eventType, NotificationEntry entry) {
- log(eventType, entry.getSbn(), entry.getRanking(), "");
- }
-
- /**
- * Logs a {@link NotifEvent} with a NotificationEntry and message
- */
- public void log(@NotifEvent.EventType int eventType, NotificationEntry entry, String msg) {
- log(eventType, entry.getSbn(), entry.getRanking(), msg);
- }
-
- /**
- * Logs a {@link NotifEvent} with a notification and message
- */
- public void log(@NotifEvent.EventType int eventType, StatusBarNotification sbn, String msg) {
- log(eventType, sbn, null, msg);
- }
-
- /**
- * Logs a {@link NotifEvent} with a ranking and message
- */
- public void log(@NotifEvent.EventType int eventType, Ranking ranking, String msg) {
- log(eventType, null, ranking, msg);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
index 8280a63dedd9..5170d0b85b17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
@@ -123,6 +123,14 @@ public final class RowContentBindParams {
}
/**
+ * Request that all content views be rebound. This may happen if, for example, the underlying
+ * layout has changed.
+ */
+ public void rebindAllContentViews() {
+ mDirtyContentViews = mContentViews;
+ }
+
+ /**
* Clears all dirty content views so that they no longer need to be rebound.
*/
void clearDirtyContentViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index db692c8a8c89..44a320419309 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -422,9 +422,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
}
private void updateDisplaySize() {
- mContext.getSystemService(DisplayManager.class)
- .getDisplay(mDisplayId)
- .getRealSize(mDisplaySize);
+ mContext.getDisplay().getRealSize(mDisplaySize);
if (mEdgeBackPlugin != null) {
mEdgeBackPlugin.setDisplaySize(mDisplaySize);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 11f70796748c..c68d9942419b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2466,10 +2466,6 @@ public class StatusBar extends SystemUI implements DemoMode,
pw.println(" mHeadsUpManager: null");
}
- if (mBubbleController != null) {
- mBubbleController.dump(fd, pw, args);
- }
-
if (mLightBarController != null) {
mLightBarController.dump(fd, pw, args);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
index cfd77be9303d..f4157f21e158 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
@@ -901,6 +901,23 @@ class PhysicsAnimator<T> private constructor (val target: T) {
verboseLogging = debug
}
+ /**
+ * Estimates the end value of a fling that starts at the given value using the provided
+ * start velocity and fling configuration.
+ *
+ * This is only an estimate. Fling animations use a timing-based physics simulation that is
+ * non-deterministic, so this exact value may not be reached.
+ */
+ @JvmStatic
+ fun estimateFlingEndValue(
+ startValue: Float,
+ startVelocity: Float,
+ flingConfig: FlingConfig
+ ): Float {
+ val distance = startVelocity / (flingConfig.friction * FLING_FRICTION_SCALAR_MULTIPLIER)
+ return Math.min(flingConfig.max, Math.max(flingConfig.min, startValue + distance))
+ }
+
@JvmStatic
fun getReadablePropertyName(property: FloatPropertyCompat<*>): String {
return when (property) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index 1954b3936376..0e9a245d5be6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -39,7 +39,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.ViewUtils;
-import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -54,7 +54,6 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
@RunWithLooper
@RunWith(AndroidTestingRunner.class)
@@ -77,8 +76,8 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
private KeyguardSecurityCallback mKeyguardCallback;
@Mock
private KeyguardUpdateMonitor mUpdateMonitor;
- @Spy
- private StubTransaction mTransaction;
+ @Mock
+ private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
@Before
public void setUp() {
@@ -97,21 +96,20 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient);
mTestController = new AdminSecondaryLockScreenController(
- mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler, mTransaction);
+ mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler);
}
@Test
public void testShow() throws Exception {
doAnswer(invocation -> {
IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1];
- callback.onSurfaceControlCreated(new SurfaceControl());
+ callback.onRemoteContentReady(mSurfacePackage);
return null;
}).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class));
mTestController.show(mServiceIntent);
verifySurfaceReady();
- verify(mTransaction).reparent(any(), any());
assertThat(mContext.isBound(mComponentName)).isTrue();
}
@@ -133,7 +131,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
// Show the view first, then hide.
doAnswer(invocation -> {
IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1];
- callback.onSurfaceControlCreated(new SurfaceControl());
+ callback.onRemoteContentReady(mSurfacePackage);
return null;
}).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class));
@@ -189,19 +187,4 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
-
- /**
- * Stubbed {@link SurfaceControl.Transaction} class that can be used when unit testing to
- * avoid calls to native code.
- */
- private class StubTransaction extends SurfaceControl.Transaction {
- @Override
- public void apply() {
- }
-
- @Override
- public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) {
- return this;
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index 486aac894d9b..c6c7b87da544 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -178,6 +178,20 @@ public class AuthContainerViewTest extends SysuiTestCase {
}
@Test
+ public void testCredentialUI_disablesClickingOnBackground() {
+ // In the credential view, clicking on the background (to cancel authentication) is not
+ // valid. Thus, the listener should be null, and it should not be in the accessibility
+ // hierarchy.
+ initializeContainer(Authenticators.DEVICE_CREDENTIAL);
+
+ mAuthContainer.onAttachedToWindowInternal();
+
+ verify(mAuthContainer.mBackgroundView).setOnClickListener(eq(null));
+ verify(mAuthContainer.mBackgroundView).setImportantForAccessibility(
+ eq(View.IMPORTANT_FOR_ACCESSIBILITY_NO));
+ }
+
+ @Test
public void testLayoutParams_hasSecureWindowFlag() {
final IBinder windowToken = mock(IBinder.class);
final WindowManager.LayoutParams layoutParams =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index d7f0f50d66db..c3b55e2ec168 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -20,8 +20,7 @@ import static android.app.Notification.FLAG_BUBBLE;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
-
-import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
+import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static com.google.common.truth.Truth.assertThat;
@@ -34,6 +33,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -55,10 +55,12 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
+import com.android.systemui.DumpController;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -68,6 +70,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -81,7 +84,6 @@ import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.InjectionInflationController;
@@ -93,6 +95,13 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
+/**
+ * Tests the NotificationEntryManager setup with BubbleController.
+ * The {@link NotifPipeline} setup with BubbleController is tested in
+ * {@link NewNotifPipelineBubbleControllerTest}.
+ */
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -152,9 +161,13 @@ public class BubbleControllerTest extends SysuiTestCase {
@Mock
private ShadeController mShadeController;
@Mock
- private RemoteInputUriController mRemoteInputUriController;
- @Mock
private NotificationRowComponent mNotificationRowComponent;
+ @Mock
+ private NotifPipeline mNotifPipeline;
+ @Mock
+ private FeatureFlags mFeatureFlagsOldPipeline;
+ @Mock
+ private DumpController mDumpController;
private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
private BubbleData mBubbleData;
@@ -216,6 +229,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mock(HeadsUpManager.class),
mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
mBubbleData = new BubbleData(mContext);
+ when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mBubbleController = new TestableBubbleController(mContext,
mNotificationShadeWindowController,
mStatusBarStateController,
@@ -227,7 +241,9 @@ public class BubbleControllerTest extends SysuiTestCase {
mLockscreenUserManager,
mNotificationGroupManager,
mNotificationEntryManager,
- mRemoteInputUriController);
+ mNotifPipeline,
+ mFeatureFlagsOldPipeline,
+ mDumpController);
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
mBubbleController.setExpandListener(mBubbleExpandListener);
@@ -265,7 +281,7 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
assertFalse(mNotificationShadeWindowController.getBubblesShowing());
assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
@@ -286,12 +302,12 @@ public class BubbleControllerTest extends SysuiTestCase {
// Now remove the bubble
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
// Since the notif is dismissed, once the bubble is removed, performRemoveNotification gets
// called to really remove the notif
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
- mRow.getEntry().getSbn(), UNDEFINED_DISMISS_REASON);
+ eq(mRow.getEntry().getSbn()), anyInt());
assertFalse(mBubbleController.hasBubbles());
}
@@ -471,7 +487,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mRow2.getEntry()));
// Dismiss currently expanded
- mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
+ mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
@@ -480,7 +496,7 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Dismiss that one
- mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
+ mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
@@ -608,7 +624,7 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED);
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_AGED);
verify(mDeleteIntent, never()).send();
}
@@ -616,7 +632,7 @@ public class BubbleControllerTest extends SysuiTestCase {
public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(1)).send();
}
@@ -653,11 +669,22 @@ public class BubbleControllerTest extends SysuiTestCase {
// Cancels always remove so no need to intercept
assertFalse(intercepted);
+ }
+
+ @Test
+ public void testRemoveBubble_entryListenerRemove() {
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+
+ // Removes the notification
+ mEntryListener.onEntryRemoved(mRow.getEntry(), null, false);
assertFalse(mBubbleController.hasBubbles());
}
@Test
- public void removeBubble_fails_clearAll() {
+ public void removeBubble_clearAllIntercepted() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
@@ -673,14 +700,10 @@ public class BubbleControllerTest extends SysuiTestCase {
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
-
- verify(mNotificationEntryManager, never()).performRemoveNotification(
- any(), anyInt());
- assertTrue(mBubbleController.hasBubbles());
}
@Test
- public void removeBubble_fails_userDismissNotif() {
+ public void removeBubble_userDismissNotifIntercepted() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
@@ -696,10 +719,6 @@ public class BubbleControllerTest extends SysuiTestCase {
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
-
- verify(mNotificationEntryManager, never()).performRemoveNotification(
- any(), anyInt());
- assertTrue(mBubbleController.hasBubbles());
}
@Test
@@ -713,7 +732,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Dismiss the bubble
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles());
// Dismiss the notification
@@ -771,6 +790,74 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
}
+ @Test
+ public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade() throws Exception {
+ // GIVEN a group summary with a bubble child
+ ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
+ ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+ mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
+ groupSummary.addChildNotification(groupedBubble);
+ assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+
+ // WHEN the summary is dismissed
+ mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+ // THEN the summary and bubbled child are suppressed from the shade
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getEntry()));
+ assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
+ }
+
+ @Test
+ public void testAppRemovesSummary_removesAllBubbleChildren() throws Exception {
+ // GIVEN a group summary with a bubble child
+ ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
+ ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+ mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
+ groupSummary.addChildNotification(groupedBubble);
+ assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+
+ // GIVEN the summary is dismissed
+ mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+ // WHEN the summary is cancelled by the app
+ mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, true);
+
+ // THEN the summary and its children are removed from bubble data
+ assertFalse(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+ assertFalse(mBubbleData.isSummarySuppressed(
+ groupSummary.getEntry().getSbn().getGroupKey()));
+ }
+
+ @Test
+ public void testSummaryDismissal_marksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
+ throws Exception {
+ // GIVEN a group summary with two (non-bubble) children and one bubble child
+ ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
+ ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+ mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
+ groupSummary.addChildNotification(groupedBubble);
+
+ // WHEN the summary is dismissed
+ mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+ // THEN only the NON-bubble children are dismissed
+ List<ExpandableNotificationRow> childrenRows = groupSummary.getNotificationChildren();
+ verify(mNotificationEntryManager, times(1)).performRemoveNotification(
+ childrenRows.get(0).getEntry().getSbn(), REASON_GROUP_SUMMARY_CANCELED);
+ verify(mNotificationEntryManager, times(1)).performRemoveNotification(
+ childrenRows.get(1).getEntry().getSbn(), REASON_GROUP_SUMMARY_CANCELED);
+ verify(mNotificationEntryManager, never()).performRemoveNotification(
+ eq(groupedBubble.getEntry().getSbn()), anyInt());
+
+ // THEN the bubble child is suppressed from the shade
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getEntry()));
+
+ // THEN the summary is removed from GroupManager
+ verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry());
+ }
+
static class TestableBubbleController extends BubbleController {
// Let's assume surfaces can be synchronized immediately.
TestableBubbleController(Context context,
@@ -784,12 +871,14 @@ public class BubbleControllerTest extends SysuiTestCase {
NotificationLockscreenUserManager lockscreenUserManager,
NotificationGroupManager groupManager,
NotificationEntryManager entryManager,
- RemoteInputUriController remoteInputUriController) {
+ NotifPipeline notifPipeline,
+ FeatureFlags featureFlags,
+ DumpController dumpController) {
super(context,
notificationShadeWindowController, statusBarStateController, shadeController,
data, Runnable::run, configurationController, interruptionStateProvider,
zenModeController, lockscreenUserManager, groupManager, entryManager,
- remoteInputUriController);
+ notifPipeline, featureFlags, dumpController);
setInflateSynchronously(true);
}
}
@@ -806,7 +895,7 @@ public class BubbleControllerTest extends SysuiTestCase {
}
/**
- * Sets the bubble metadata flags for this entry. These flags are normally set by
+ * Sets the bubble metadata flags for this entry. These ]flags are normally set by
* NotificationManagerService when the notification is sent, however, these tests do not
* go through that path so we set them explicitly when testing.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
new file mode 100644
index 000000000000..72405fc519fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -0,0 +1,842 @@
+/*
+ * 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 static android.app.Notification.FLAG_BUBBLE;
+import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.IActivityManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.face.FaceManager;
+import android.service.notification.ZenModeConfig;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.colorextraction.ColorExtractor;
+import com.android.systemui.DumpController;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.SuperStatusBarViewFactory;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.InjectionInflationController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+/**
+ * Tests the NotifPipeline setup with BubbleController.
+ * The NotificationEntryManager setup with BubbleController is tested in
+ * {@link BubbleControllerTest}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
+ @Mock
+ private NotificationEntryManager mNotificationEntryManager;
+ @Mock
+ private NotificationGroupManager mNotificationGroupManager;
+ @Mock
+ private BubbleController.NotifCallback mNotifCallback;
+ @Mock
+ private WindowManager mWindowManager;
+ @Mock
+ private IActivityManager mActivityManager;
+ @Mock
+ private DozeParameters mDozeParameters;
+ @Mock
+ private ConfigurationController mConfigurationController;
+ @Mock
+ private ZenModeController mZenModeController;
+ @Mock
+ private ZenModeConfig mZenModeConfig;
+ @Mock
+ private FaceManager mFaceManager;
+ @Mock
+ private NotificationLockscreenUserManager mLockscreenUserManager;
+ @Mock
+ private SysuiStatusBarStateController mStatusBarStateController;
+ @Mock
+ private KeyguardBypassController mKeyguardBypassController;
+
+ @Captor
+ private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
+
+ private TestableBubbleController mBubbleController;
+ private NotificationShadeWindowController mNotificationShadeWindowController;
+ private NotifCollectionListener mEntryListener;
+
+ private NotificationTestHelper mNotificationTestHelper;
+ private ExpandableNotificationRow mRow;
+ private ExpandableNotificationRow mRow2;
+ private ExpandableNotificationRow mNonBubbleNotifRow;
+
+ @Mock
+ private BubbleController.BubbleStateChangeListener mBubbleStateChangeListener;
+ @Mock
+ private BubbleController.BubbleExpandListener mBubbleExpandListener;
+ @Mock
+ private PendingIntent mDeleteIntent;
+ @Mock
+ private SysuiColorExtractor mColorExtractor;
+ @Mock
+ ColorExtractor.GradientColors mGradientColors;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private ShadeController mShadeController;
+ @Mock
+ private NotificationRowComponent mNotificationRowComponent;
+ @Mock
+ private NotifPipeline mNotifPipeline;
+ @Mock
+ private FeatureFlags mFeatureFlagsNewPipeline;
+ @Mock
+ private DumpController mDumpController;
+
+ private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
+ private BubbleData mBubbleData;
+
+ private TestableLooper mTestableLooper;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTestableLooper = TestableLooper.get(this);
+
+ mContext.addMockSystemService(FaceManager.class, mFaceManager);
+ when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
+
+ mSuperStatusBarViewFactory = new SuperStatusBarViewFactory(mContext,
+ new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()),
+ new NotificationRowComponent.Builder() {
+ @Override
+ public NotificationRowComponent.Builder activatableNotificationView(
+ ActivatableNotificationView view) {
+ return this;
+ }
+
+ @Override
+ public NotificationRowComponent build() {
+ return mNotificationRowComponent;
+ }
+ });
+
+ // Bubbles get added to status bar window view
+ mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
+ mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
+ mConfigurationController, mKeyguardBypassController, mColorExtractor,
+ mSuperStatusBarViewFactory);
+ mNotificationShadeWindowController.attach();
+
+ // Need notifications for bubbles
+ mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
+ mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
+ mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
+ mNonBubbleNotifRow = mNotificationTestHelper.createRow();
+
+ mZenModeConfig.suppressedVisualEffects = 0;
+ when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
+
+ TestableNotificationInterruptionStateProvider interruptionStateProvider =
+ new TestableNotificationInterruptionStateProvider(mContext,
+ mock(NotificationFilter.class),
+ mock(StatusBarStateController.class),
+ mock(BatteryController.class));
+ interruptionStateProvider.setUpWithPresenter(
+ mock(NotificationPresenter.class),
+ mock(HeadsUpManager.class),
+ mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
+ mBubbleData = new BubbleData(mContext);
+ when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
+ mBubbleController = new TestableBubbleController(mContext,
+ mNotificationShadeWindowController,
+ mStatusBarStateController,
+ mShadeController,
+ mBubbleData,
+ mConfigurationController,
+ interruptionStateProvider,
+ mZenModeController,
+ mLockscreenUserManager,
+ mNotificationGroupManager,
+ mNotificationEntryManager,
+ mNotifPipeline,
+ mFeatureFlagsNewPipeline,
+ mDumpController);
+ mBubbleController.addNotifCallback(mNotifCallback);
+ mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
+ mBubbleController.setExpandListener(mBubbleExpandListener);
+
+ // Get a reference to the BubbleController's entry listener
+ verify(mNotifPipeline, atLeastOnce())
+ .addCollectionListener(mNotifListenerCaptor.capture());
+ mEntryListener = mNotifListenerCaptor.getValue();
+ }
+
+ @Test
+ public void testAddBubble() {
+ mBubbleController.updateBubble(mRow.getEntry());
+ assertTrue(mBubbleController.hasBubbles());
+
+ verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+ }
+
+ @Test
+ public void testHasBubbles() {
+ assertFalse(mBubbleController.hasBubbles());
+ mBubbleController.updateBubble(mRow.getEntry());
+ assertTrue(mBubbleController.hasBubbles());
+ }
+
+ @Test
+ public void testRemoveBubble() {
+ mBubbleController.updateBubble(mRow.getEntry());
+ assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ assertTrue(mBubbleController.hasBubbles());
+ verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
+ verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ assertFalse(mNotificationShadeWindowController.getBubblesShowing());
+ assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
+ verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
+ }
+
+ @Test
+ public void testRemoveBubble_withDismissedNotif() {
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+
+ // Make it look like dismissed notif
+ mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
+
+ // Now remove the bubble
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+
+ // Since the notif is dismissed, once the bubble is removed, removeNotification gets
+ // called to really remove the notif
+ verify(mNotifCallback, times(1)).removeNotification(eq(mRow.getEntry()), anyInt());
+ assertFalse(mBubbleController.hasBubbles());
+ }
+
+ @Test
+ public void testDismissStack() {
+ mBubbleController.updateBubble(mRow.getEntry());
+ verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
+ assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ mBubbleController.updateBubble(mRow2.getEntry());
+ verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
+ assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+ assertTrue(mBubbleController.hasBubbles());
+
+ mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ assertFalse(mNotificationShadeWindowController.getBubblesShowing());
+ verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
+ assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+ }
+
+ @Test
+ public void testExpandCollapseStack() {
+ assertFalse(mBubbleController.isStackExpanded());
+
+ // Mark it as a bubble and add it explicitly
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ // We should have bubbles & their notifs should not be suppressed
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+ assertFalse(mNotificationShadeWindowController.getBubbleExpanded());
+
+ // Expand the stack
+ BubbleStackView stackView = mBubbleController.getStackView();
+ mBubbleController.expandStack();
+ assertTrue(mBubbleController.isStackExpanded());
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+ assertTrue(mNotificationShadeWindowController.getBubbleExpanded());
+
+ // Make sure the notif is suppressed
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+
+ // Collapse
+ mBubbleController.collapseStack();
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
+ assertFalse(mBubbleController.isStackExpanded());
+ assertFalse(mNotificationShadeWindowController.getBubbleExpanded());
+ }
+
+ @Test
+ public void testCollapseAfterChangingExpandedBubble() {
+ // Mark it as a bubble and add it explicitly
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mEntryListener.onEntryAdded(mRow2.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mRow2.getEntry());
+
+ // We should have bubbles & their notifs should not be suppressed
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow2.getEntry()));
+
+ // Expand
+ BubbleStackView stackView = mBubbleController.getStackView();
+ mBubbleController.expandStack();
+ assertTrue(mBubbleController.isStackExpanded());
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
+
+ // Last added is the one that is expanded
+ assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry()));
+
+ // Switch which bubble is expanded
+ mBubbleController.selectBubble(mRow.getEntry().getKey());
+ mBubbleData.setExpanded(true);
+ assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+
+ // collapse for previous bubble
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
+ // expand for selected bubble
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+
+ // Collapse
+ mBubbleController.collapseStack();
+ assertFalse(mBubbleController.isStackExpanded());
+ }
+
+ @Test
+ public void testExpansionRemovesShowInShadeAndDot() {
+ // Mark it as a bubble and add it explicitly
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ // We should have bubbles & their notifs should not be suppressed
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+
+ mTestableLooper.processAllMessages();
+ assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+
+ // Expand
+ mBubbleController.expandStack();
+ assertTrue(mBubbleController.isStackExpanded());
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+
+ // Notif is suppressed after expansion
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+ // Notif shouldn't show dot after expansion
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ }
+
+ @Test
+ public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
+ // Mark it as a bubble and add it explicitly
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ // We should have bubbles & their notifs should not be suppressed
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+
+ mTestableLooper.processAllMessages();
+ assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+
+ // Expand
+ mBubbleController.expandStack();
+ assertTrue(mBubbleController.isStackExpanded());
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+
+ // Notif is suppressed after expansion
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+ // Notif shouldn't show dot after expansion
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+
+ // Send update
+ mEntryListener.onEntryUpdated(mRow.getEntry());
+
+ // Nothing should have changed
+ // Notif is suppressed after expansion
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+ // Notif shouldn't show dot after expansion
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ }
+
+ @Test
+ public void testRemoveLastExpandedCollapses() {
+ // Mark it as a bubble and add it explicitly
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mEntryListener.onEntryAdded(mRow2.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mRow2.getEntry());
+ verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+
+ // Expand
+ BubbleStackView stackView = mBubbleController.getStackView();
+ mBubbleController.expandStack();
+
+ assertTrue(mBubbleController.isStackExpanded());
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
+
+ // Last added is the one that is expanded
+ assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow2.getEntry()));
+
+ // Dismiss currently expanded
+ mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
+ BubbleController.DISMISS_USER_GESTURE);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
+
+ // Make sure first bubble is selected
+ assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+
+ // Dismiss that one
+ mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
+ BubbleController.DISMISS_USER_GESTURE);
+
+ // Make sure state changes and collapse happens
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
+ verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
+ assertFalse(mBubbleController.hasBubbles());
+ }
+
+ @Test
+ public void testAutoExpand_fails_noFlag() {
+ assertFalse(mBubbleController.isStackExpanded());
+ setMetadataFlags(mRow.getEntry(),
+ Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
+
+ // Add the auto expand bubble
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ // Expansion shouldn't change
+ verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
+ mRow.getEntry().getKey());
+ assertFalse(mBubbleController.isStackExpanded());
+
+ // # of bubbles should change
+ verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+ }
+
+ @Test
+ public void testAutoExpand_succeeds_withFlag() {
+ setMetadataFlags(mRow.getEntry(),
+ Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
+
+ // Add the auto expand bubble
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ // Expansion should change
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
+ mRow.getEntry().getKey());
+ assertTrue(mBubbleController.isStackExpanded());
+
+ // # of bubbles should change
+ verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+ }
+
+ @Test
+ public void testSuppressNotif_onInitialNotif() {
+ setMetadataFlags(mRow.getEntry(),
+ Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
+
+ // Add the suppress notif bubble
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ // Notif should be suppressed because we were foreground
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+ // Dot + flyout is hidden because notif is suppressed
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
+
+ // # of bubbles should change
+ verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+ }
+
+ @Test
+ public void testSuppressNotif_onUpdateNotif() {
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ // Should not be suppressed
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+ // Should show dot
+ assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+
+ // Update to suppress notif
+ setMetadataFlags(mRow.getEntry(),
+ Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ // Notif should be suppressed
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+ // Dot + flyout is hidden because notif is suppressed
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
+
+ // # of bubbles should change
+ verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+ }
+
+ @Test
+ public void testMarkNewNotificationAsShowInShade() {
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+
+ mTestableLooper.processAllMessages();
+ assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+ }
+
+ @Test
+ public void testAddNotif_notBubble() {
+ mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
+ mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
+
+ verify(mBubbleStateChangeListener, never()).onHasBubblesChanged(anyBoolean());
+ assertThat(mBubbleController.hasBubbles()).isFalse();
+ }
+
+ @Test
+ public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
+ mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_AGED);
+ verify(mDeleteIntent, never()).send();
+ }
+
+ @Test
+ public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
+ mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.removeBubble(
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ verify(mDeleteIntent, times(1)).send();
+ }
+
+ @Test
+ public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
+ mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mRow2.getEntry());
+ mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ verify(mDeleteIntent, times(2)).send();
+ }
+
+ @Test
+ public void testRemoveBubble_noLongerBubbleAfterUpdate()
+ throws PendingIntent.CanceledException {
+ mBubbleController.updateBubble(mRow.getEntry());
+ assertTrue(mBubbleController.hasBubbles());
+
+ mRow.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
+ mEntryListener.onEntryUpdated(mRow.getEntry());
+
+ assertFalse(mBubbleController.hasBubbles());
+ verify(mDeleteIntent, never()).send();
+ }
+
+ @Test
+ public void testRemoveBubble_entryListenerRemove() {
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+
+ // Removes the notification
+ mEntryListener.onEntryRemoved(mRow.getEntry(), 0);
+ assertFalse(mBubbleController.hasBubbles());
+ }
+
+ @Test
+ public void removeBubble_intercepted() {
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+
+ boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
+
+ // Intercept!
+ assertTrue(intercepted);
+ // Should update show in shade state
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+ }
+
+ @Test
+ public void removeBubble_succeeds_userDismissBubble_userDimissNotif() {
+ mEntryListener.onEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+
+ // Dismiss the bubble
+ mBubbleController.removeBubble(
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ assertFalse(mBubbleController.hasBubbles());
+
+ // Dismiss the notification
+ boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
+
+ // It's no longer a bubble so we shouldn't intercept
+ assertFalse(intercepted);
+ }
+
+ @Test
+ public void testNotifyShadeSuppressionChange_notificationDismiss() {
+ BubbleController.NotificationSuppressionChangedListener listener =
+ mock(BubbleController.NotificationSuppressionChangedListener.class);
+ mBubbleData.setSuppressionChangedListener(listener);
+
+ mEntryListener.onEntryAdded(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+
+ mBubbleController.handleDismissalInterception(mRow.getEntry());
+
+ // Should update show in shade state
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+
+ // Should notify delegate that shade state changed
+ verify(listener).onBubbleNotificationSuppressionChange(
+ mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ }
+
+ @Test
+ public void testNotifyShadeSuppressionChange_bubbleExpanded() {
+ BubbleController.NotificationSuppressionChangedListener listener =
+ mock(BubbleController.NotificationSuppressionChangedListener.class);
+ mBubbleData.setSuppressionChangedListener(listener);
+
+ mEntryListener.onEntryAdded(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+
+ mBubbleData.setExpanded(true);
+
+ // Once a bubble is expanded the notif is suppressed
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry()));
+
+ // Should notify delegate that shade state changed
+ verify(listener).onBubbleNotificationSuppressionChange(
+ mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ }
+
+ @Test
+ public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade() throws Exception {
+ // GIVEN a group summary with a bubble child
+ ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
+ ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+ mEntryListener.onEntryAdded(groupedBubble.getEntry());
+ groupSummary.addChildNotification(groupedBubble);
+ assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+
+ // WHEN the summary is dismissed
+ mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+ // THEN the summary and bubbled child are suppressed from the shade
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getEntry()));
+ assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
+ }
+
+ @Test
+ public void testAppRemovesSummary_removesAllBubbleChildren() throws Exception {
+ // GIVEN a group summary with a bubble child
+ ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
+ ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+ mEntryListener.onEntryAdded(groupedBubble.getEntry());
+ groupSummary.addChildNotification(groupedBubble);
+ assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+
+ // GIVEN the summary is dismissed
+ mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+ // WHEN the summary is cancelled by the app
+ mEntryListener.onEntryRemoved(groupSummary.getEntry(), 0);
+
+ // THEN the summary and its children are removed from bubble data
+ assertFalse(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+ assertFalse(mBubbleData.isSummarySuppressed(
+ groupSummary.getEntry().getSbn().getGroupKey()));
+ }
+
+ @Test
+ public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
+ throws Exception {
+ // GIVEN a group summary with two (non-bubble) children and one bubble child
+ ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
+ ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+ mEntryListener.onEntryAdded(groupedBubble.getEntry());
+ groupSummary.addChildNotification(groupedBubble);
+
+ // WHEN the summary is dismissed
+ mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+ // THEN only the NON-bubble children are dismissed
+ List<ExpandableNotificationRow> childrenRows = groupSummary.getNotificationChildren();
+ verify(mNotifCallback, times(1)).removeNotification(
+ childrenRows.get(0).getEntry(), REASON_GROUP_SUMMARY_CANCELED);
+ verify(mNotifCallback, times(1)).removeNotification(
+ childrenRows.get(1).getEntry(), REASON_GROUP_SUMMARY_CANCELED);
+ verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()), anyInt());
+
+ // THEN the bubble child still exists as a bubble and is suppressed from the shade
+ assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getEntry()));
+
+ // THEN the summary is also suppressed from the shade
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ groupSummary.getEntry()));
+ }
+
+ static class TestableBubbleController extends BubbleController {
+ // Let's assume surfaces can be synchronized immediately.
+ TestableBubbleController(Context context,
+ NotificationShadeWindowController notificationShadeWindowController,
+ StatusBarStateController statusBarStateController,
+ ShadeController shadeController,
+ BubbleData data,
+ ConfigurationController configurationController,
+ NotificationInterruptionStateProvider interruptionStateProvider,
+ ZenModeController zenModeController,
+ NotificationLockscreenUserManager lockscreenUserManager,
+ NotificationGroupManager groupManager,
+ NotificationEntryManager entryManager,
+ NotifPipeline notifPipeline,
+ FeatureFlags featureFlags,
+ DumpController dumpController) {
+ super(context,
+ notificationShadeWindowController, statusBarStateController, shadeController,
+ data, Runnable::run, configurationController, interruptionStateProvider,
+ zenModeController, lockscreenUserManager, groupManager, entryManager,
+ notifPipeline, featureFlags, dumpController);
+ setInflateSynchronously(true);
+ }
+ }
+
+ static class TestableNotificationInterruptionStateProvider extends
+ NotificationInterruptionStateProvider {
+
+ TestableNotificationInterruptionStateProvider(Context context,
+ NotificationFilter filter, StatusBarStateController controller,
+ BatteryController batteryController) {
+ super(context, filter, controller, batteryController);
+ mUseHeadsUp = true;
+ }
+ }
+
+ /**
+ * Sets the bubble metadata flags for this entry. These flags are normally set by
+ * NotificationManagerService when the notification is sent, however, these tests do not
+ * go through that path so we set them explicitly when testing.
+ */
+ private void setMetadataFlags(NotificationEntry entry, int flag, boolean enableFlag) {
+ Notification.BubbleMetadata bubbleMetadata =
+ entry.getSbn().getNotification().getBubbleMetadata();
+ int flags = bubbleMetadata.getFlags();
+ if (enableFlag) {
+ flags |= flag;
+ } else {
+ flags &= ~flag;
+ }
+ bubbleMetadata.setFlags(flags);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java
deleted file mode 100644
index 4a90bb91ca37..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.log;
-
-import static junit.framework.Assert.assertEquals;
-
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import junit.framework.Assert;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class RichEventTest extends SysuiTestCase {
-
- private static final int TOTAL_EVENT_TYPES = 1;
-
- @Test
- public void testCreateRichEvent_invalidType() {
- try {
- // indexing for events starts at 0, so TOTAL_EVENT_TYPES is an invalid type
- new TestableRichEvent(Event.DEBUG, TOTAL_EVENT_TYPES, "msg");
- } catch (IllegalArgumentException e) {
- // expected
- return;
- }
-
- Assert.fail("Expected an invalidArgumentException since the event type was invalid.");
- }
-
- @Test
- public void testCreateRichEvent() {
- final int eventType = 0;
- RichEvent e = new TestableRichEvent(Event.DEBUG, eventType, "msg");
- assertEquals(e.getType(), eventType);
- }
-
- class TestableRichEvent extends RichEvent {
- TestableRichEvent(int logLevel, int type, String reason) {
- init(logLevel, type, reason);
- }
-
- @Override
- public String[] getEventLabels() {
- return new String[]{"ACTION_NAME"};
- }
- }
-
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
deleted file mode 100644
index e7b317e882ef..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.log;
-
-import static junit.framework.Assert.assertEquals;
-
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.DumpController;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class SysuiLogTest extends SysuiTestCase {
- private static final String TEST_ID = "TestLogger";
- private static final String TEST_MSG = "msg";
- private static final int MAX_LOGS = 5;
-
- @Mock
- private DumpController mDumpController;
- private SysuiLog<Event> mSysuiLog;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testLogDisabled_noLogsWritten() {
- mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, false);
- assertEquals(null, mSysuiLog.mTimeline);
-
- mSysuiLog.log(createEvent(TEST_MSG));
- assertEquals(null, mSysuiLog.mTimeline);
- }
-
- @Test
- public void testLogEnabled_logWritten() {
- mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
- assertEquals(0, mSysuiLog.mTimeline.size());
-
- mSysuiLog.log(createEvent(TEST_MSG));
- assertEquals(1, mSysuiLog.mTimeline.size());
- }
-
- @Test
- public void testMaxLogs() {
- mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
- assertEquals(mSysuiLog.mTimeline.size(), 0);
-
- for (int i = 0; i < MAX_LOGS + 1; i++) {
- mSysuiLog.log(createEvent(TEST_MSG + i));
- }
-
- assertEquals(MAX_LOGS, mSysuiLog.mTimeline.size());
-
- // check the first message (msg0) was replaced with msg1:
- assertEquals(TEST_MSG + "1", mSysuiLog.mTimeline.getFirst().getMessage());
- }
-
- @Test
- public void testRecycleLogs() {
- // GIVEN a SysuiLog with one log
- mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
- Event e = createEvent(TEST_MSG); // msg
- mSysuiLog.log(e); // Logs: [msg]
-
- Event recycledEvent = null;
- // WHEN we add MAX_LOGS after the first log
- for (int i = 0; i < MAX_LOGS; i++) {
- recycledEvent = mSysuiLog.log(createEvent(TEST_MSG + i));
- }
- // Logs: [msg1, msg2, msg3, msg4]
-
- // THEN we see the recycledEvent is e
- assertEquals(e, recycledEvent);
- }
-
- private Event createEvent(String msg) {
- return new Event().init(msg);
- }
-
- public class TestSysuiLog extends SysuiLog<Event> {
- protected TestSysuiLog(DumpController dumpController, String id, int maxLogs,
- boolean enabled) {
- super(dumpController, id, maxLogs, enabled, false);
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 4becd522ebd6..9fe2569177d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -88,8 +88,8 @@ class CustomTileTest : SysuiTestCase() {
}
@Test
- fun testBooleanTileHasBooleanState() {
- `when`(mTileServiceManager.isBooleanTile).thenReturn(true)
+ fun testToggleableTileHasBooleanState() {
+ `when`(mTileServiceManager.isToggleableTile).thenReturn(true)
customTile = CustomTile.create(mTileHost, TILE_SPEC)
assertTrue(customTile.state is QSTile.BooleanState)
@@ -104,7 +104,7 @@ class CustomTileTest : SysuiTestCase() {
@Test
fun testValueUpdatedInBooleanTile() {
- `when`(mTileServiceManager.isBooleanTile).thenReturn(true)
+ `when`(mTileServiceManager.isToggleableTile).thenReturn(true)
customTile = CustomTile.create(mTileHost, TILE_SPEC)
customTile.qsTile.icon = mock(Icon::class.java)
`when`(customTile.qsTile.icon.loadDrawable(any(Context::class.java)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index 9e5e582bf5e7..42fd288d94ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -105,7 +105,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
defaultServiceInfo = new ServiceInfo();
defaultServiceInfo.metaData = new Bundle();
defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, true);
- defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_BOOLEAN_TILE, true);
+ defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_TOGGLEABLE_TILE, true);
}
when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt(), anyInt()))
.thenReturn(defaultServiceInfo);
@@ -244,7 +244,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
}
@Test
- public void testBooleanTile() throws Exception {
- assertTrue(mStateManager.isBooleanTile());
+ public void testToggleableTile() throws Exception {
+ assertTrue(mStateManager.isToggleableTile());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 0a3bc6def160..1d4b4be9e683 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -53,8 +53,8 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitor.BatteryStatus;
import com.android.settingslib.Utils;
+import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index b51581f544f5..07f6936ece07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -79,7 +79,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
@@ -139,7 +138,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private RowInflaterTask mAsyncInflationTask;
- @Mock private NotifLog mNotifLog;
+ @Mock private NotificationEntryManagerLogger mLogger;
@Mock private FeatureFlags mFeatureFlags;
@Mock private LeakDetector mLeakDetector;
@Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
@@ -234,14 +233,14 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mEntryManager = new TestableNotificationEntryManager(
- mNotifLog,
+ mLogger,
mGroupManager,
new NotificationRankingManager(
() -> mock(NotificationMediaManager.class),
mGroupManager,
mHeadsUpManager,
mock(NotificationFilter.class),
- mNotifLog,
+ mLogger,
mock(NotificationSectionsFeatureManager.class),
mock(PeopleNotificationIdentifier.class),
mock(HighPriorityProvider.class)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
index a9f9db67ff0b..0e730e5c3ffb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
@@ -22,7 +22,6 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder
-import com.android.systemui.statusbar.notification.logging.NotifLog
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.NotificationGroupManager
@@ -34,7 +33,7 @@ import java.util.concurrent.CountDownLatch
* Enable some test capabilities for NEM without making everything public on the base class
*/
class TestableNotificationEntryManager(
- log: NotifLog,
+ logger: NotificationEntryManagerLogger,
gm: NotificationGroupManager,
rm: NotificationRankingManager,
ke: KeyguardEnvironment,
@@ -43,7 +42,7 @@ class TestableNotificationEntryManager(
notificationRemoteInputManagerLazy: dagger.Lazy<NotificationRemoteInputManager>,
leakDetector: LeakDetector,
fgsFeatureController: ForegroundServiceDismissalFeatureController
-) : NotificationEntryManager(log, gm, rm, ke, ff, rb,
+) : NotificationEntryManager(logger, gm, rm, ke, ff, rb,
notificationRemoteInputManagerLazy, leakDetector, fgsFeatureController) {
public var countDownLatch: CountDownLatch = CountDownLatch(1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 7c94ed20e95a..abc0f3ee8a52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -34,6 +34,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -47,6 +48,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationStats;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
@@ -69,6 +71,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.Assert;
@@ -98,11 +101,19 @@ public class NotifCollectionTest extends SysuiTestCase {
@Spy private RecordingCollectionListener mCollectionListener;
@Mock private CollectionReadyForBuildListener mBuildListener;
@Mock private FeatureFlags mFeatureFlags;
+ @Mock private DismissedByUserStats mDismissedByUserStats;
@Spy private RecordingLifetimeExtender mExtender1 = new RecordingLifetimeExtender("Extender1");
@Spy private RecordingLifetimeExtender mExtender2 = new RecordingLifetimeExtender("Extender2");
@Spy private RecordingLifetimeExtender mExtender3 = new RecordingLifetimeExtender("Extender3");
+ @Spy private RecordingDismissInterceptor mInterceptor1 = new RecordingDismissInterceptor(
+ "Interceptor1");
+ @Spy private RecordingDismissInterceptor mInterceptor2 = new RecordingDismissInterceptor(
+ "Interceptor2");
+ @Spy private RecordingDismissInterceptor mInterceptor3 = new RecordingDismissInterceptor(
+ "Interceptor3");
+
@Captor private ArgumentCaptor<BatchableNotificationHandler> mListenerCaptor;
@Captor private ArgumentCaptor<NotificationEntry> mEntryCaptor;
@Captor private ArgumentCaptor<Collection<NotificationEntry>> mBuildListCaptor;
@@ -441,6 +452,169 @@ public class NotifCollectionTest extends SysuiTestCase {
assertEquals(NOT_DISMISSED, entry3.getDismissState());
}
+ @Test
+ public void testDismissInterceptorsAreCalled() throws RemoteException {
+ // GIVEN a collection with notifications with multiple dismiss interceptors
+ mInterceptor1.shouldInterceptDismissal = true;
+ mInterceptor2.shouldInterceptDismissal = true;
+ mInterceptor3.shouldInterceptDismissal = false;
+ mCollection.addNotificationDismissInterceptor(mInterceptor1);
+ mCollection.addNotificationDismissInterceptor(mInterceptor2);
+ mCollection.addNotificationDismissInterceptor(mInterceptor3);
+
+ NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+ NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+ // WHEN a notification is manually dismissed
+ DismissedByUserStats stats = new DismissedByUserStats(
+ NotificationStats.DISMISSAL_SHADE,
+ NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
+ mCollection.dismissNotification(entry, stats);
+
+ // THEN all interceptors get checked
+ verify(mInterceptor1).shouldInterceptDismissal(entry);
+ verify(mInterceptor2).shouldInterceptDismissal(entry);
+ verify(mInterceptor3).shouldInterceptDismissal(entry);
+ assertEquals(List.of(mInterceptor1, mInterceptor2), entry.mDismissInterceptors);
+
+ // THEN we never send the dismissal to system server
+ verify(mStatusBarService, never()).onNotificationClear(
+ notif.sbn.getPackageName(),
+ notif.sbn.getTag(),
+ 47,
+ notif.sbn.getUser().getIdentifier(),
+ notif.sbn.getKey(),
+ stats.dismissalSurface,
+ stats.dismissalSentiment,
+ stats.notificationVisibility);
+ }
+
+ @Test
+ public void testDismissInterceptorsCanceledWhenNotifIsUpdated() throws RemoteException {
+ // GIVEN a few lifetime extenders and a couple notifications
+ mCollection.addNotificationDismissInterceptor(mInterceptor1);
+ mCollection.addNotificationDismissInterceptor(mInterceptor2);
+
+ mInterceptor1.shouldInterceptDismissal = true;
+ mInterceptor2.shouldInterceptDismissal = true;
+
+ NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+ NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+ // WHEN a notification is manually dismissed and intercepted
+ DismissedByUserStats stats = new DismissedByUserStats(
+ NotificationStats.DISMISSAL_SHADE,
+ NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
+ mCollection.dismissNotification(entry, stats);
+ assertEquals(List.of(mInterceptor1, mInterceptor2), entry.mDismissInterceptors);
+ clearInvocations(mInterceptor1, mInterceptor2);
+
+ // WHEN the notification is reposted
+ mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+
+ // THEN all of the active dismissal interceptors are canceled
+ verify(mInterceptor1).cancelDismissInterception(entry);
+ verify(mInterceptor2).cancelDismissInterception(entry);
+ assertEquals(List.of(), entry.mDismissInterceptors);
+
+ // THEN the notification is never sent to system server to dismiss
+ verify(mStatusBarService, never()).onNotificationClear(
+ eq(notif.sbn.getPackageName()),
+ eq(notif.sbn.getTag()),
+ eq(47),
+ eq(notif.sbn.getUser().getIdentifier()),
+ eq(notif.sbn.getKey()),
+ anyInt(),
+ anyInt(),
+ anyObject());
+ }
+
+ @Test
+ public void testEndingAllDismissInterceptorsSendsDismiss() throws RemoteException {
+ // GIVEN a collection with notifications a dismiss interceptor
+ mInterceptor1.shouldInterceptDismissal = true;
+ mCollection.addNotificationDismissInterceptor(mInterceptor1);
+
+ NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+ NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+ // GIVEN a notification is manually dismissed
+ DismissedByUserStats stats = new DismissedByUserStats(
+ NotificationStats.DISMISSAL_SHADE,
+ NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
+ mCollection.dismissNotification(entry, stats);
+
+ // WHEN all interceptors end their interception dismissal
+ mInterceptor1.shouldInterceptDismissal = false;
+ mInterceptor1.onEndInterceptionCallback.onEndDismissInterception(mInterceptor1, entry,
+ mDismissedByUserStats);
+
+ // THEN we send the dismissal to system server
+ verify(mStatusBarService, times(1)).onNotificationClear(
+ eq(notif.sbn.getPackageName()),
+ eq(notif.sbn.getTag()),
+ eq(47),
+ eq(notif.sbn.getUser().getIdentifier()),
+ eq(notif.sbn.getKey()),
+ anyInt(),
+ anyInt(),
+ anyObject());
+ }
+
+ @Test
+ public void testEndDismissInterceptionUpdatesDismissInterceptors() throws RemoteException {
+ // GIVEN a collection with notifications with multiple dismiss interceptors
+ mInterceptor1.shouldInterceptDismissal = true;
+ mInterceptor2.shouldInterceptDismissal = true;
+ mInterceptor3.shouldInterceptDismissal = false;
+ mCollection.addNotificationDismissInterceptor(mInterceptor1);
+ mCollection.addNotificationDismissInterceptor(mInterceptor2);
+ mCollection.addNotificationDismissInterceptor(mInterceptor3);
+
+ NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+ NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+ // GIVEN a notification is manually dismissed
+ DismissedByUserStats stats = new DismissedByUserStats(
+ NotificationStats.DISMISSAL_SHADE,
+ NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
+ mCollection.dismissNotification(entry, stats);
+
+ // WHEN an interceptor ends its interception
+ mInterceptor1.shouldInterceptDismissal = false;
+ mInterceptor1.onEndInterceptionCallback.onEndDismissInterception(mInterceptor1, entry,
+ mDismissedByUserStats);
+
+ // THEN all interceptors get checked
+ verify(mInterceptor1).shouldInterceptDismissal(entry);
+ verify(mInterceptor2).shouldInterceptDismissal(entry);
+ verify(mInterceptor3).shouldInterceptDismissal(entry);
+
+ // THEN mInterceptor2 is the only dismiss interceptor
+ assertEquals(List.of(mInterceptor2), entry.mDismissInterceptors);
+ }
+
+
+ @Test(expected = IllegalStateException.class)
+ public void testEndingDismissalOfNonInterceptedThrows() throws RemoteException {
+ // GIVEN a collection with notifications with a dismiss interceptor that hasn't been called
+ mInterceptor1.shouldInterceptDismissal = false;
+ mCollection.addNotificationDismissInterceptor(mInterceptor1);
+
+ NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+ NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+ // WHEN we try to end the dismissal of an interceptor that didn't intercept the notif
+ mInterceptor1.onEndInterceptionCallback.onEndDismissInterception(mInterceptor1, entry,
+ mDismissedByUserStats);
+
+ // THEN an exception is thrown
+ }
+
@Test(expected = IllegalStateException.class)
public void testDismissingNonExistentNotificationThrows() {
// GIVEN a collection that originally had three notifs, but where one was dismissed
@@ -894,6 +1068,36 @@ public class NotifCollectionTest extends SysuiTestCase {
}
}
+ private static class RecordingDismissInterceptor implements NotifDismissInterceptor {
+ private final String mName;
+
+ public @Nullable OnEndDismissInterception onEndInterceptionCallback;
+ public boolean shouldInterceptDismissal = false;
+
+ private RecordingDismissInterceptor(String name) {
+ mName = name;
+ }
+
+ @Override
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public void setCallback(OnEndDismissInterception callback) {
+ this.onEndInterceptionCallback = callback;
+ }
+
+ @Override
+ public boolean shouldInterceptDismissal(NotificationEntry entry) {
+ return shouldInterceptDismissal;
+ }
+
+ @Override
+ public void cancelDismissInterception(NotificationEntry entry) {
+ }
+ }
+
private static final String TEST_PACKAGE = "com.android.test.collection";
private static final String TEST_PACKAGE2 = "com.android.test.collection2";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index 7ab4846ea066..c6b496dd8215 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -27,10 +27,10 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
-import com.android.systemui.statusbar.notification.logging.NotifLog
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
@@ -62,7 +62,7 @@ class NotificationRankingManagerTest : SysuiTestCase() {
mock(NotificationGroupManager::class.java),
mock(HeadsUpManager::class.java),
mock(NotificationFilter::class.java),
- mock(NotifLog::class.java),
+ mock(NotificationEntryManagerLogger::class.java),
mock(NotificationSectionsFeatureManager::class.java),
personNotificationIdentifier,
HighPriorityProvider(personNotificationIdentifier)
@@ -189,7 +189,7 @@ class NotificationRankingManagerTest : SysuiTestCase() {
groupManager: NotificationGroupManager,
headsUpManager: HeadsUpManager,
filter: NotificationFilter,
- notifLog: NotifLog,
+ logger: NotificationEntryManagerLogger,
sectionsFeatureManager: NotificationSectionsFeatureManager,
peopleNotificationIdentifier: PeopleNotificationIdentifier,
highPriorityProvider: HighPriorityProvider
@@ -198,7 +198,7 @@ class NotificationRankingManagerTest : SysuiTestCase() {
groupManager,
headsUpManager,
filter,
- notifLog,
+ logger,
sectionsFeatureManager,
peopleNotificationIdentifier,
highPriorityProvider
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 3d9832de417a..9b2e0c375e87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -199,9 +199,17 @@ public class NotificationTestHelper {
/**
* Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
*/
+ public ExpandableNotificationRow createBubbleInGroup()
+ throws Exception {
+ return createBubble(makeBubbleMetadata(null), PKG, true);
+ }
+
+ /**
+ * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
+ */
public ExpandableNotificationRow createBubble()
throws Exception {
- return createBubble(makeBubbleMetadata(null), PKG);
+ return createBubble(makeBubbleMetadata(null), PKG, false);
}
/**
@@ -211,7 +219,7 @@ public class NotificationTestHelper {
*/
public ExpandableNotificationRow createBubble(@Nullable PendingIntent deleteIntent)
throws Exception {
- return createBubble(makeBubbleMetadata(deleteIntent), PKG);
+ return createBubble(makeBubbleMetadata(deleteIntent), PKG, false);
}
/**
@@ -221,8 +229,14 @@ public class NotificationTestHelper {
*/
public ExpandableNotificationRow createBubble(BubbleMetadata bubbleMetadata, String pkg)
throws Exception {
+ return createBubble(bubbleMetadata, pkg, false);
+ }
+
+ private ExpandableNotificationRow createBubble(BubbleMetadata bubbleMetadata, String pkg,
+ boolean inGroup)
+ throws Exception {
Notification n = createNotification(false /* isGroupSummary */,
- null /* groupKey */, bubbleMetadata);
+ inGroup ? GROUP_KEY : null /* groupKey */, bubbleMetadata);
n.flags |= FLAG_BUBBLE;
ExpandableNotificationRow row = generateRow(n, pkg, UID, USER_HANDLE,
0 /* extraInflationFlags */, IMPORTANCE_HIGH);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index 66aa5e18d0c9..775f722b13f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -67,7 +67,7 @@ public class RowContentBindStageTest extends SysuiTestCase {
}
@Test
- public void testSetShouldContentViewsBeBound_bindsContent() {
+ public void testRequireContentViews() {
// WHEN inflation flags are set and pipeline is invalidated.
final int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
@@ -85,7 +85,7 @@ public class RowContentBindStageTest extends SysuiTestCase {
}
@Test
- public void testSetShouldContentViewsBeBound_unbindsContent() {
+ public void testFreeContentViews() {
// GIVEN a view with all content bound.
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
params.requireContentViews(FLAG_CONTENT_VIEW_ALL);
@@ -100,6 +100,28 @@ public class RowContentBindStageTest extends SysuiTestCase {
}
@Test
+ public void testRebindAllContentViews() {
+ // GIVEN a view with content bound.
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ final int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
+ params.requireContentViews(flags);
+ params.clearDirtyContentViews();
+
+ // WHEN we request rebind and stage executed.
+ params.rebindAllContentViews();
+ mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
+
+ // THEN binder binds inflation flags.
+ verify(mBinder).bindContent(
+ eq(mEntry),
+ any(),
+ eq(flags),
+ any(),
+ anyBoolean(),
+ any());
+ }
+
+ @Test
public void testSetUseLowPriority() {
// GIVEN a view with all content bound.
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
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 70d76f0c3a52..b16e52ce7bd4 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
@@ -65,6 +65,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.TestableNotificationEntryManager;
@@ -74,7 +75,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
@@ -163,14 +163,14 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor
.forClass(UserChangedListener.class);
mEntryManager = new TestableNotificationEntryManager(
- mock(NotifLog.class),
+ mock(NotificationEntryManagerLogger.class),
mock(NotificationGroupManager.class),
new NotificationRankingManager(
() -> mock(NotificationMediaManager.class),
mGroupManager,
mHeadsUpManager,
mock(NotificationFilter.class),
- mock(NotifLog.class),
+ mock(NotificationEntryManagerLogger.class),
mock(NotificationSectionsFeatureManager.class),
mock(PeopleNotificationIdentifier.class),
mock(HighPriorityProvider.class)
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index e0adb34dad6c..8c4f733fb62a 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -59,16 +59,33 @@ java_library {
],
hostdex: true, // for hiddenapi check
- visibility: [
- "//frameworks/base/packages/Tethering:__subpackages__",
- //TODO(b/147200698) remove below lines when the platform is built with stubs
- "//frameworks/base",
- "//frameworks/base/services",
- "//frameworks/base/services/core",
- ],
+ visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
apex_available: ["com.android.tethering"],
}
+droidstubs {
+ name: "framework-tethering-stubs-sources",
+ defaults: ["framework-module-stubs-defaults-module_libs_api"],
+ srcs: [
+ "src/android/net/TetheredClient.java",
+ "src/android/net/TetheringManager.java",
+ "src/android/net/TetheringConstants.java",
+ ],
+ libs: [
+ "tethering-aidl-interfaces-java",
+ "framework-all",
+ ],
+ sdk_version: "core_platform",
+}
+
+java_library {
+ name: "framework-tethering-stubs",
+ srcs: [":framework-tethering-stubs-sources"],
+ libs: ["framework-all"],
+ static_libs: ["tethering-aidl-interfaces-java"],
+ sdk_version: "core_platform",
+}
+
filegroup {
name: "framework-tethering-srcs",
srcs: [
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 651468846ca8..ca5ef09d6ad9 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -34,6 +36,7 @@ import java.util.Objects;
* @hide
*/
@SystemApi
+@SystemApi(client = MODULE_LIBRARIES)
@TestApi
public final class TetheredClient implements Parcelable {
@NonNull
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index 00cf98e0fc2d..df87ac994d42 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -16,6 +16,9 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
import android.os.ResultReceiver;
/**
@@ -28,39 +31,30 @@ import android.os.ResultReceiver;
* symbols from framework-tethering even when they are in a non-hidden class.
* @hide
*/
+@SystemApi(client = MODULE_LIBRARIES)
public class TetheringConstants {
/**
* Extra used for communicating with the TetherService. Includes the type of tethering to
* enable if any.
- *
- * {@hide}
*/
public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
/**
* Extra used for communicating with the TetherService. Includes the type of tethering for
* which to cancel provisioning.
- *
- * {@hide}
*/
public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
/**
* Extra used for communicating with the TetherService. True to schedule a recheck of tether
* provisioning.
- *
- * {@hide}
*/
public static final String EXTRA_SET_ALARM = "extraSetAlarm";
/**
* Tells the TetherService to run a provision check now.
- *
- * {@hide}
*/
public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
/**
* Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
* which will receive provisioning results. Can be left empty.
- *
- * {@hide}
*/
public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 53a358f29a97..6a9f010449c4 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -15,6 +15,8 @@
*/
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -50,6 +52,7 @@ import java.util.function.Supplier;
* @hide
*/
@SystemApi
+@SystemApi(client = MODULE_LIBRARIES)
@TestApi
public class TetheringManager {
private static final String TAG = TetheringManager.class.getSimpleName();
@@ -177,6 +180,7 @@ public class TetheringManager {
* service is not connected.
* {@hide}
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public TetheringManager(@NonNull final Context context,
@NonNull Supplier<IBinder> connectorSupplier) {
mContext = context;
@@ -395,6 +399,7 @@ public class TetheringManager {
* {@hide}
*/
@Deprecated
+ @SystemApi(client = MODULE_LIBRARIES)
public int tether(@NonNull final String iface) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "tether caller:" + callerPkg);
@@ -418,6 +423,7 @@ public class TetheringManager {
* {@hide}
*/
@Deprecated
+ @SystemApi(client = MODULE_LIBRARIES)
public int untether(@NonNull final String iface) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "untether caller:" + callerPkg);
@@ -444,6 +450,7 @@ public class TetheringManager {
* {@hide}
*/
@Deprecated
+ @SystemApi(client = MODULE_LIBRARIES)
public int setUsbTethering(final boolean enable) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "setUsbTethering caller:" + callerPkg);
@@ -702,6 +709,7 @@ public class TetheringManager {
* {@hide}
*/
// TODO: improve the usage of ResultReceiver, b/145096122
+ @SystemApi(client = MODULE_LIBRARIES)
public void requestLatestTetheringEntitlementResult(final int type,
@NonNull final ResultReceiver receiver, final boolean showEntitlementUi) {
final String callerPkg = mContext.getOpPackageName();
@@ -982,6 +990,7 @@ public class TetheringManager {
* interface
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public int getLastTetherError(@NonNull final String iface) {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR;
@@ -1004,6 +1013,7 @@ public class TetheringManager {
* what interfaces are considered tetherable usb interfaces.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableUsbRegexs() {
mCallback.waitForStarted();
return mTetheringConfiguration.tetherableUsbRegexs;
@@ -1018,6 +1028,7 @@ public class TetheringManager {
* what interfaces are considered tetherable wifi interfaces.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableWifiRegexs() {
mCallback.waitForStarted();
return mTetheringConfiguration.tetherableWifiRegexs;
@@ -1032,6 +1043,7 @@ public class TetheringManager {
* what interfaces are considered tetherable bluetooth interfaces.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableBluetoothRegexs() {
mCallback.waitForStarted();
return mTetheringConfiguration.tetherableBluetoothRegexs;
@@ -1044,6 +1056,7 @@ public class TetheringManager {
* @return an array of 0 or more Strings of tetherable interface names.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableIfaces() {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
@@ -1057,6 +1070,7 @@ public class TetheringManager {
* @return an array of 0 or more String of currently tethered interface names.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetheredIfaces() {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
@@ -1076,6 +1090,7 @@ public class TetheringManager {
* which failed to tether.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetheringErroredIfaces() {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
@@ -1103,6 +1118,7 @@ public class TetheringManager {
* @return a boolean - {@code true} indicating Tethering is supported.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public boolean isTetheringSupported() {
final String callerPkg = mContext.getOpPackageName();
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 39c402be84a3..64c16e41dbf5 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -1944,7 +1944,8 @@ public class Tethering {
parcel.tetheringSupported = mDeps.isTetheringSupported();
parcel.upstreamNetwork = mTetherUpstream;
parcel.config = mConfig.toStableParcelable();
- parcel.states = mTetherStatesParcel;
+ parcel.states =
+ mTetherStatesParcel != null ? mTetherStatesParcel : emptyTetherStatesParcel();
try {
callback.onCallbackStarted(parcel);
} catch (RemoteException e) {
@@ -1953,6 +1954,17 @@ public class Tethering {
});
}
+ private TetherStatesParcel emptyTetherStatesParcel() {
+ final TetherStatesParcel parcel = new TetherStatesParcel();
+ parcel.availableList = new String[0];
+ parcel.tetheredList = new String[0];
+ parcel.localOnlyList = new String[0];
+ parcel.erroredIfaceList = new String[0];
+ parcel.lastErrorList = new int[0];
+
+ return parcel;
+ }
+
/** Unregister tethering event callback */
void unregisterTetheringEventCallback(ITetheringEventCallback callback) {
mHandler.post(() -> {
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 13174c5bb57a..c6905ec8efce 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -34,7 +34,15 @@ android_test {
"TetheringApiCurrentLib",
"testables",
],
+ // TODO(b/147200698) change sdk_version to module-current and
+ // remove framework-minus-apex, ext, and framework-res
+ sdk_version: "core_platform",
libs: [
+ "framework-minus-apex",
+ "ext",
+ "framework-res",
+ "framework-wifi-stubs",
+ "framework-telephony-stubs",
"android.test.runner",
"android.test.base",
"android.test.mock",
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 4710287f33f3..6d49e20e5753 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -127,6 +127,7 @@ import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.networkstack.tethering.R;
+import com.android.testutils.MiscAssertsKt;
import org.junit.After;
import org.junit.Before;
@@ -1220,6 +1221,16 @@ public class TetheringTest {
}
}
+ private void assertTetherStatesNotNullButEmpty(final TetherStatesParcel parcel) {
+ assertFalse(parcel == null);
+ assertEquals(0, parcel.availableList.length);
+ assertEquals(0, parcel.tetheredList.length);
+ assertEquals(0, parcel.localOnlyList.length);
+ assertEquals(0, parcel.erroredIfaceList.length);
+ assertEquals(0, parcel.lastErrorList.length);
+ MiscAssertsKt.assertFieldCountEquals(5, TetherStatesParcel.class);
+ }
+
@Test
public void testRegisterTetheringEventCallback() throws Exception {
TestTetheringEventCallback callback = new TestTetheringEventCallback();
@@ -1232,7 +1243,7 @@ public class TetheringTest {
callback.expectConfigurationChanged(
mTethering.getTetheringConfiguration().toStableParcelable());
TetherStatesParcel tetherState = callback.pollTetherStatesChanged();
- assertEquals(tetherState, null);
+ assertTetherStatesNotNullButEmpty(tetherState);
// 2. Enable wifi tethering.
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index fc7709c4a51e..dcdb80b497d0 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -28,6 +28,7 @@ LOCAL_REQUIRED_MODULES := \
DisplayCutoutEmulationDoubleOverlay \
DisplayCutoutEmulationHoleOverlay \
DisplayCutoutEmulationTallOverlay \
+ DisplayCutoutEmulationWaterfallOverlay \
FontNotoSerifSourceOverlay \
IconPackCircularAndroidOverlay \
IconPackCircularLauncherOverlay \
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.mk
new file mode 100644
index 000000000000..b6b6dd1c25bc
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := DisplayCutoutEmulationWaterfall
+
+
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationWaterfallOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/AndroidManifest.xml
new file mode 100644
index 000000000000..2d5bb14f4dd3
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.internal.display.cutout.emulation.waterfall"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android"
+ android:category="com.android.internal.display_cutout_emulation"
+ android:priority="1"/>
+
+ <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml
new file mode 100644
index 000000000000..df2f3d19626e
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ 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>
+ <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
+ <dimen name="quick_qs_offset_height">48dp</dimen>
+ <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
+ <dimen name="quick_qs_total_height">176dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
new file mode 100644
index 000000000000..6f692c8021c0
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
@@ -0,0 +1,35 @@
+<!--
+ ~ 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">
+
+ <!-- Height of the status bar in portrait. The height should be
+ Max((status bar content height + waterfall top size), top cutout size) -->
+ <dimen name="status_bar_height_portrait">28dp</dimen>
+ <!-- Max((28 + 20), 0) = 48 -->
+ <dimen name="status_bar_height_landscape">48dp</dimen>
+ <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
+ <dimen name="quick_qs_offset_height">28dp</dimen>
+ <!-- Total height of QQS (quick_qs_offset_height + 128) -->
+ <dimen name="quick_qs_total_height">156dp</dimen>
+
+ <dimen name="waterfall_display_left_edge_size">20dp</dimen>
+ <dimen name="waterfall_display_top_edge_size">0dp</dimen>
+ <dimen name="waterfall_display_right_edge_size">20dp</dimen>
+ <dimen name="waterfall_display_bottom_edge_size">0dp</dimen>
+</resources>
+
+
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/strings.xml
new file mode 100644
index 000000000000..ed073d0e244e
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/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="display_cutout_emulation_overlay">Waterfall cutout</string>
+
+</resources> \ No newline at end of file
diff --git a/services/Android.bp b/services/Android.bp
index 28c8aeea4e53..32394f4fe5d9 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -75,7 +75,7 @@ java_library {
libs: [
"android.hidl.manager-V1.0-java",
- "framework-tethering"
+ "framework-tethering-stubs",
],
plugins: [
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index a5877ccbde7c..565ee63d89ab 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID;
+import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER;
import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
@@ -36,10 +38,11 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
-import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
@@ -50,6 +53,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -71,6 +75,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -106,6 +111,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
private final PowerManager mPowerManager;
private final IPlatformCompat mIPlatformCompat;
+ private final Handler mMainHandler;
+
// Handler for scheduling method invocations on the main thread.
public final InvocationHandler mInvocationHandler;
@@ -238,6 +245,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
mSecurityPolicy = securityPolicy;
mSystemActionPerformer = systemActionPerfomer;
mSystemSupport = systemSupport;
+ mMainHandler = mainHandler;
mInvocationHandler = new InvocationHandler(mainHandler.getLooper());
mA11yWindowManager = a11yWindowManager;
mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
@@ -959,52 +967,72 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
}
- @Nullable
@Override
- public Bitmap takeScreenshot(int displayId) {
+ public void takeScreenshot(int displayId, RemoteCallback callback) {
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
- return null;
+ sendScreenshotResult(true, null, callback);
+ return;
}
if (!mSecurityPolicy.canTakeScreenshotLocked(this)) {
- return null;
+ sendScreenshotResult(true, null, callback);
+ throw new SecurityException("Services don't have the capability of taking"
+ + " the screenshot.");
}
}
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
- return null;
+ sendScreenshotResult(true, null, callback);
+ return;
}
final Display display = DisplayManagerGlobal.getInstance()
.getRealDisplay(displayId);
if (display == null) {
- return null;
+ sendScreenshotResult(true, null, callback);
+ return;
}
- final Point displaySize = new Point();
- display.getRealSize(displaySize);
- final int rotation = display.getRotation();
- Bitmap screenShot = null;
+ sendScreenshotResult(false, display, callback);
+ }
+ private void sendScreenshotResult(boolean noResult, Display display, RemoteCallback callback) {
+ final boolean noScreenshot = noResult;
final long identity = Binder.clearCallingIdentity();
try {
- final Rect crop = new Rect(0, 0, displaySize.x, displaySize.y);
- // TODO (b/145893483): calling new API with the display as a parameter
- // when surface control supported.
- screenShot = SurfaceControl.screenshot(crop, displaySize.x, displaySize.y,
- rotation);
- if (screenShot != null) {
- // Optimization for telling the bitmap that all of the pixels are known to be
- // opaque (false). This is meant as a drawing hint, as in some cases a bitmap
- // that is known to be opaque can take a faster drawing case than one that may
- // have non-opaque per-pixel alpha values.
- screenShot.setHasAlpha(false);
- }
+ mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
+ if (noScreenshot) {
+ callback.sendResult(null);
+ return;
+ }
+ final Point displaySize = new Point();
+ // TODO (b/145893483): calling new API with the display as a parameter
+ // when surface control supported.
+ final IBinder token = SurfaceControl.getInternalDisplayToken();
+ final Rect crop = new Rect(0, 0, displaySize.x, displaySize.y);
+ final int rotation = display.getRotation();
+ display.getRealSize(displaySize);
+
+ final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
+ SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, crop,
+ displaySize.x, displaySize.y, false,
+ rotation);
+ final GraphicBuffer graphicBuffer = screenshotBuffer.getGraphicBuffer();
+ final HardwareBuffer hardwareBuffer =
+ HardwareBuffer.createFromGraphicBuffer(graphicBuffer);
+ final int colorSpaceId = screenshotBuffer.getColorSpace().getId();
+
+ // Send back the result.
+ final Bundle payload = new Bundle();
+ payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
+ hardwareBuffer);
+ payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID, colorSpaceId);
+ callback.sendResult(payload);
+ }, null).recycleOnUse());
} finally {
Binder.restoreCallingIdentity(identity);
}
- return screenShot;
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 25911a7ed0ea..edb4445151d5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -16,8 +16,6 @@
package com.android.server.accessibility;
-import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT;
-
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.Manifest;
@@ -27,20 +25,16 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
-import android.graphics.Bitmap;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.Display;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -393,15 +387,4 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
}
}
-
- @Override
- public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {
- mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
- final Bitmap screenshot = super.takeScreenshot(displayId);
- // Send back the result.
- final Bundle payload = new Bundle();
- payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT, screenshot);
- callback.sendResult(payload);
- }, null).recycleOnUse());
- }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 5d9af26a8339..d1c3a02c6761 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -328,6 +328,6 @@ class UiAutomationManager {
public void onFingerprintGesture(int gesture) {}
@Override
- public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
+ public void takeScreenshot(int displayId, RemoteCallback callback) {}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 1cb9313d9bf9..4474f608f955 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -82,6 +82,7 @@ import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.infra.AbstractPerUserSystemService;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -180,6 +181,8 @@ final class AutofillManagerServiceImpl
private final InputMethodManagerInternal mInputMethodManagerInternal;
+ private final ContentCaptureManagerInternal mContentCaptureManagerInternal;
+
AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
AutofillCompatState autofillCompatState,
@@ -192,10 +195,22 @@ final class AutofillManagerServiceImpl
mFieldClassificationStrategy = new FieldClassificationStrategy(getContext(), userId);
mAutofillCompatState = autofillCompatState;
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
+ mContentCaptureManagerInternal = LocalServices.getService(
+ ContentCaptureManagerInternal.class);
updateLocked(disabled);
}
+ boolean sendActivityAssistDataToContentCapture(@NonNull IBinder activityToken,
+ @NonNull Bundle data) {
+ if (mContentCaptureManagerInternal != null) {
+ mContentCaptureManagerInternal.sendActivityAssistData(getUserId(), activityToken, data);
+ return true;
+ }
+
+ return false;
+ }
+
@GuardedBy("mLock")
void onBackKeyPressed() {
final RemoteAugmentedAutofillService remoteService =
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 880c40158114..d93ac68bde1e 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -166,8 +166,8 @@ final class RemoteAugmentedAutofillService
public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) {
mCallbacks.resetLastResponse();
maybeRequestShowInlineSuggestions(sessionId,
- inlineSuggestionsData, focusedId,
- inlineSuggestionsCallback, client,
+ inlineSuggestionsRequest, inlineSuggestionsData,
+ focusedId, inlineSuggestionsCallback, client,
onErrorCallback);
requestAutofill.complete(null);
}
@@ -231,10 +231,12 @@ final class RemoteAugmentedAutofillService
}
private void maybeRequestShowInlineSuggestions(int sessionId,
- @Nullable Dataset[] inlineSuggestionsData, @NonNull AutofillId focusedId,
+ @Nullable InlineSuggestionsRequest request, @Nullable Dataset[] inlineSuggestionsData,
+ @NonNull AutofillId focusedId,
@Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
@NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback) {
- if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null) {
+ if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null
+ || request == null) {
return;
}
mCallbacks.setLastResponse(sessionId);
@@ -242,7 +244,7 @@ final class RemoteAugmentedAutofillService
try {
inlineSuggestionsCallback.onInlineSuggestionsResponse(
InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
- inlineSuggestionsData, focusedId, mContext,
+ request, inlineSuggestionsData, focusedId, mContext,
dataset -> {
mCallbacks.logAugmentedAutofillSelected(sessionId,
dataset.getId());
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 7e5123c82054..a25d7353edcb 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -314,6 +314,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private final InputMethodManagerInternal mInputMethodManagerInternal;
@Nullable
+ @GuardedBy("mLock")
private InlineSuggestionsRequestCallbackImpl mInlineSuggestionsRequestCallback;
/**
@@ -423,6 +424,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
suggestionsRequest);
}
+ if (mActivityToken != null) {
+ mService.sendActivityAssistDataToContentCapture(mActivityToken, resultData);
+ }
mRemoteFillService.onFillRequest(request);
}
@@ -2674,11 +2678,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (response.supportsInlineSuggestions()) {
- if (requestShowInlineSuggestions(response)) {
- //TODO(b/137800469): Fix it to log showed only when IME asks for inflation, rather
- // than here where framework sends back the response.
- mService.logDatasetShown(id, mClientState);
- return;
+ synchronized (mLock) {
+ if (requestShowInlineSuggestions(response, mInlineSuggestionsRequestCallback)) {
+ //TODO(b/137800469): Fix it to log showed only when IME asks for inflation,
+ // rather than here where framework sends back the response.
+ mService.logDatasetShown(id, mClientState);
+ return;
+ }
}
}
@@ -2716,30 +2722,32 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/**
* Returns whether we made a request to show inline suggestions.
*/
- private boolean requestShowInlineSuggestions(FillResponse response) {
- final IInlineSuggestionsResponseCallback inlineContentCallback =
- mInlineSuggestionsRequestCallback != null
- ? mInlineSuggestionsRequestCallback.getResponseCallback() : null;
- if (inlineContentCallback == null) {
- Log.w(TAG, "Session input method callback is not set yet");
- return false;
- }
-
+ private boolean requestShowInlineSuggestions(@NonNull FillResponse response,
+ @Nullable InlineSuggestionsRequestCallbackImpl callback) {
final List<Dataset> datasets = response.getDatasets();
if (datasets == null) {
Log.w(TAG, "response returned null datasets");
return false;
}
+ if (callback == null || callback.getRequest() == null
+ || callback.getResponseCallback() == null) {
+ Log.w(TAG, "Session input method callback is not set yet");
+ return false;
+ }
+
+ final InlineSuggestionsRequest request = callback.getRequest();
InlineSuggestionsResponse inlineSuggestionsResponse =
- InlineSuggestionFactory.createInlineSuggestionsResponse(response.getRequestId(),
- datasets.toArray(new Dataset[]{}), mCurrentViewId, mContext, this, () -> {
- synchronized (mLock) {
- requestHideFillUi(mCurrentViewId);
- }
- });
- try {
- inlineContentCallback.onInlineSuggestionsResponse(inlineSuggestionsResponse);
+ InlineSuggestionFactory.createInlineSuggestionsResponse(request,
+ response.getRequestId(),
+ datasets.toArray(new Dataset[]{}), response.getInlineActions(),
+ mCurrentViewId, mContext, this, () -> {
+ synchronized (mLock) {
+ requestHideFillUi(mCurrentViewId);
+ }
+ });
+ try {
+ callback.getResponseCallback().onInlineSuggestionsResponse(inlineSuggestionsResponse);
} catch (RemoteException e) {
Log.w(TAG, "onFillReady() remote error calling onInlineSuggestionsResponse()");
return false;
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 38a5b5b1cdaa..95a4a191a52e 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -28,16 +28,20 @@ import android.util.Slog;
import android.view.SurfaceControl;
import android.view.View;
import android.view.autofill.AutofillId;
+import android.view.inline.InlinePresentationSpec;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionInfo;
+import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
+import android.widget.Toast;
-import com.android.internal.util.function.QuadFunction;
import com.android.internal.view.inline.IInlineContentCallback;
import com.android.internal.view.inline.IInlineContentProvider;
import com.android.server.UiThread;
import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
public final class InlineSuggestionFactory {
private static final String TAG = "InlineSuggestionFactory";
@@ -57,46 +61,47 @@ public final class InlineSuggestionFactory {
* augmented autofill service.
*/
public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
+ @NonNull InlineSuggestionsRequest request,
@NonNull Dataset[] datasets,
@NonNull AutofillId autofillId,
@NonNull Context context,
@NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
@NonNull Runnable onErrorCallback) {
- return createInlineSuggestionsResponseInternal(datasets, autofillId,
- context, onErrorCallback, (dataset, inlinePresentation, inlineSuggestionUi,
- filedIndex) -> createAugmentedInlineSuggestion(dataset,
- inlinePresentation, inlineSuggestionUi, inlineSuggestionUiCallback));
+ if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
+ return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
+ datasets, /* inlineActions= */ null, autofillId, context, onErrorCallback,
+ (dataset, filedIndex) -> (v -> inlineSuggestionUiCallback.autofill(dataset)));
}
/**
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
* autofill service.
*/
- public static InlineSuggestionsResponse createInlineSuggestionsResponse(int requestId,
+ public static InlineSuggestionsResponse createInlineSuggestionsResponse(
+ @NonNull InlineSuggestionsRequest request, int requestId,
@NonNull Dataset[] datasets,
+ @Nullable List<InlinePresentation> inlineActions,
@NonNull AutofillId autofillId,
@NonNull Context context,
@NonNull AutoFillUI.AutoFillUiCallback client,
@NonNull Runnable onErrorCallback) {
- return createInlineSuggestionsResponseInternal(datasets, autofillId,
- context, onErrorCallback, (dataset, inlinePresentation, inlineSuggestionUi,
- filedIndex) -> createInlineSuggestion(requestId, dataset, filedIndex,
- inlinePresentation, inlineSuggestionUi, client));
+ if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
+ return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, datasets,
+ inlineActions, autofillId, context, onErrorCallback,
+ (dataset, filedIndex) -> (v -> client.fill(requestId, filedIndex, dataset)));
}
private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
- @NonNull Dataset[] datasets,
- @NonNull AutofillId autofillId,
- @NonNull Context context,
+ boolean isAugmented, @NonNull InlineSuggestionsRequest request,
+ @NonNull Dataset[] datasets, @Nullable List<InlinePresentation> inlineActions,
+ @NonNull AutofillId autofillId, @NonNull Context context,
@NonNull Runnable onErrorCallback,
- @NonNull QuadFunction<Dataset, InlinePresentation, InlineSuggestionUi,
- Integer, InlineSuggestion> suggestionFactory) {
- if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
-
+ @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context,
onErrorCallback);
- for (Dataset dataset : datasets) {
+ for (int i = 0; i < datasets.length; i++) {
+ final Dataset dataset = datasets[i];
final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
if (fieldIndex < 0) {
Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
@@ -108,50 +113,82 @@ public final class InlineSuggestionFactory {
Slog.w(TAG, "InlinePresentation not found in dataset");
return null;
}
- InlineSuggestion inlineSuggestion = suggestionFactory.apply(dataset,
- inlinePresentation, inlineSuggestionUi, fieldIndex);
+ InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
+ fieldIndex, mergedInlinePresentation(request, i, inlinePresentation),
+ inlineSuggestionUi, onClickListenerFactory);
inlineSuggestions.add(inlineSuggestion);
}
+ if (inlineActions != null) {
+ for (InlinePresentation inlinePresentation : inlineActions) {
+ final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
+ mergedInlinePresentation(request, 0, inlinePresentation),
+ inlineSuggestionUi);
+ inlineSuggestions.add(inlineAction);
+ }
+ }
return new InlineSuggestionsResponse(inlineSuggestions);
}
- private static InlineSuggestion createAugmentedInlineSuggestion(@NonNull Dataset dataset,
+ private static InlineSuggestion createInlineAction(boolean isAugmented,
+ @NonNull Context context,
@NonNull InlinePresentation inlinePresentation,
- @NonNull InlineSuggestionUi inlineSuggestionUi,
- @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback) {
+ @NonNull InlineSuggestionUi inlineSuggestionUi) {
// TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
- InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""},
- InlineSuggestionInfo.TYPE_SUGGESTION);
- final View.OnClickListener onClickListener = v ->
- inlineSuggestionUiCallback.autofill(dataset);
- final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
+ isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
+ : InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
+ InlineSuggestionInfo.TYPE_ACTION);
+ final View.OnClickListener onClickListener = v -> {
+ // TODO(b/148567875): Launch the intent provided through the slice. This
+ // should be part of the UI renderer therefore will be moved to the support
+ // library.
+ Toast.makeText(context, "icon clicked", Toast.LENGTH_SHORT).show();
+ };
+ return new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
onClickListener));
- return inlineSuggestion;
}
- private static InlineSuggestion createInlineSuggestion(int requestId,
+ private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
@NonNull Dataset dataset,
- int fieldIndex,
- @NonNull InlinePresentation inlinePresentation,
+ int fieldIndex, @NonNull InlinePresentation inlinePresentation,
@NonNull InlineSuggestionUi inlineSuggestionUi,
- @NonNull AutoFillUI.AutoFillUiCallback client) {
+ @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
// TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
- InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
+ isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
+ : InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
InlineSuggestionInfo.TYPE_SUGGESTION);
- final View.OnClickListener onClickListener = v -> {
- client.fill(requestId, fieldIndex, dataset);
- };
+ final View.OnClickListener onClickListener = onClickListenerFactory.apply(dataset,
+ fieldIndex);
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
onClickListener));
return inlineSuggestion;
}
+ /**
+ * Returns an {@link InlinePresentation} with the style spec from the request/host, and
+ * everything else from the provided {@code inlinePresentation}.
+ */
+ private static InlinePresentation mergedInlinePresentation(
+ @NonNull InlineSuggestionsRequest request,
+ int index, @NonNull InlinePresentation inlinePresentation) {
+ final List<InlinePresentationSpec> specs = request.getPresentationSpecs();
+ if (specs.isEmpty()) {
+ return inlinePresentation;
+ }
+ InlinePresentationSpec specFromHost = specs.get(Math.min(specs.size() - 1, index));
+ InlinePresentationSpec mergedInlinePresentation = new InlinePresentationSpec.Builder(
+ inlinePresentation.getInlinePresentationSpec().getMinSize(),
+ inlinePresentation.getInlinePresentationSpec().getMaxSize()).setStyle(
+ specFromHost.getStyle()).build();
+ return new InlinePresentation(inlinePresentation.getSlice(), mergedInlinePresentation,
+ inlinePresentation.isPinned());
+ }
+
private static IInlineContentProvider.Stub createInlineContentProvider(
@NonNull InlinePresentation inlinePresentation,
@NonNull InlineSuggestionUi inlineSuggestionUi,
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 583c5b593b88..32bca35009a8 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -426,18 +426,26 @@ final class ContentCapturePerUserService
public boolean sendActivityAssistDataLocked(@NonNull IBinder activityToken,
@NonNull Bundle data) {
final int id = getSessionId(activityToken);
+ final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
+ final AssistStructure assistStructure = data.getParcelable(ASSIST_KEY_STRUCTURE);
+ final AssistContent assistContent = data.getParcelable(ASSIST_KEY_CONTENT);
+ final SnapshotData snapshotData = new SnapshotData(assistData,
+ assistStructure, assistContent);
if (id != NO_SESSION_ID) {
final ContentCaptureServerSession session = mSessions.get(id);
- final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
- final AssistStructure assistStructure = data.getParcelable(ASSIST_KEY_STRUCTURE);
- final AssistContent assistContent = data.getParcelable(ASSIST_KEY_CONTENT);
- final SnapshotData snapshotData = new SnapshotData(assistData,
- assistStructure, assistContent);
session.sendActivitySnapshotLocked(snapshotData);
return true;
- } else {
- Slog.e(TAG, "Failed to notify activity assist data for activity: " + activityToken);
}
+
+ // We want to send an activity snapshot regardless of whether a content capture session is
+ // present or not since a content capture session is not required for this functionality
+ if (mRemoteService != null) {
+ mRemoteService.onActivitySnapshotRequest(NO_SESSION_ID, snapshotData);
+ Slog.d(TAG, "Notified activity assist data for activity: "
+ + activityToken + " without a session Id");
+ return true;
+ }
+
return false;
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index f33237f490e4..6fc6084de181 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -98,7 +98,7 @@ java_library_static {
"android.hardware.tv.cec-V1.0-java",
"android.hardware.vibrator-java",
"app-compat-annotations",
- "framework-tethering",
+ "framework-tethering-stubs",
],
required: [
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 60f420e17e3b..e17c1f8f8276 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -24,7 +24,7 @@ import android.bluetooth.BluetoothProfile;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
import android.media.AudioManager;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
@@ -402,7 +402,7 @@ import java.io.PrintWriter;
}
/*package*/ int setPreferredDeviceForStrategySync(int strategy,
- @NonNull AudioDeviceAddress device) {
+ @NonNull AudioDevice device) {
return mDeviceInventory.setPreferredDeviceForStrategySync(strategy, device);
}
@@ -543,7 +543,7 @@ import java.io.PrintWriter;
sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
}
- /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy, AudioDeviceAddress device)
+ /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy, AudioDevice device)
{
sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy, device);
}
@@ -904,7 +904,7 @@ import java.io.PrintWriter;
} break;
case MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY: {
final int strategy = msg.arg1;
- final AudioDeviceAddress device = (AudioDeviceAddress) msg.obj;
+ final AudioDevice device = (AudioDevice) msg.obj;
mDeviceInventory.onSaveSetPreferredDevice(strategy, device);
} break;
case MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY: {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 75d9dd817487..1f998c377c7b 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -23,7 +23,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
import android.media.AudioDevicePort;
import android.media.AudioFormat;
import android.media.AudioManager;
@@ -75,7 +75,7 @@ public class AudioDeviceInventory {
private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>();
// List of preferred devices for strategies
- private final ArrayMap<Integer, AudioDeviceAddress> mPreferredDevices = new ArrayMap<>();
+ private final ArrayMap<Integer, AudioDevice> mPreferredDevices = new ArrayMap<>();
// the wrapper for AudioSystem static methods, allows us to spy AudioSystem
private final @NonNull AudioSystemAdapter mAudioSystem;
@@ -468,7 +468,7 @@ public class AudioDeviceInventory {
}
}
- /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAddress device) {
+ /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDevice device) {
mPreferredDevices.put(strategy, device);
}
@@ -480,7 +480,7 @@ public class AudioDeviceInventory {
//
/*package*/ int setPreferredDeviceForStrategySync(int strategy,
- @NonNull AudioDeviceAddress device) {
+ @NonNull AudioDevice device) {
final long identity = Binder.clearCallingIdentity();
final int status = mAudioSystem.setPreferredDeviceForStrategy(strategy, device);
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 82a2f01d4857..342ce22066b6 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -65,7 +65,7 @@ import android.hardware.usb.UsbManager;
import android.hidl.manager.V1_0.IServiceManager;
import android.media.AudioAttributes;
import android.media.AudioAttributes.AttributeSystemUsage;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
import android.media.AudioDeviceInfo;
import android.media.AudioFocusInfo;
import android.media.AudioFocusRequest;
@@ -1712,7 +1712,7 @@ public class AudioService extends IAudioService.Stub
// IPC methods
///////////////////////////////////////////////////////////////////////////
/** @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceInfo) */
- public int setPreferredDeviceForStrategy(int strategy, AudioDeviceAddress device) {
+ public int setPreferredDeviceForStrategy(int strategy, AudioDevice device) {
if (device == null) {
return AudioSystem.ERROR;
}
@@ -1721,7 +1721,7 @@ public class AudioService extends IAudioService.Stub
"setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
Binder.getCallingUid(), Binder.getCallingPid(), strategy, device.toString());
sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
- if (device.getRole() == AudioDeviceAddress.ROLE_INPUT) {
+ if (device.getRole() == AudioDevice.ROLE_INPUT) {
Log.e(TAG, "Unsupported input routing in " + logString);
return AudioSystem.ERROR;
}
@@ -1749,9 +1749,9 @@ public class AudioService extends IAudioService.Stub
}
/** @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy) */
- public AudioDeviceAddress getPreferredDeviceForStrategy(int strategy) {
+ public AudioDevice getPreferredDeviceForStrategy(int strategy) {
enforceModifyAudioRoutingPermission();
- AudioDeviceAddress[] devices = new AudioDeviceAddress[1];
+ AudioDevice[] devices = new AudioDevice[1];
final long identity = Binder.clearCallingIdentity();
final int status = AudioSystem.getPreferredDeviceForStrategy(strategy, devices);
Binder.restoreCallingIdentity(identity);
@@ -1765,7 +1765,7 @@ public class AudioService extends IAudioService.Stub
}
/** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
- public @NonNull ArrayList<AudioDeviceAddress> getDevicesForAttributes(
+ public @NonNull ArrayList<AudioDevice> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
Objects.requireNonNull(attributes);
enforceModifyAudioRoutingPermission();
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 9d06b4257b62..a3086c050af1 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -17,7 +17,7 @@
package com.android.server.audio;
import android.annotation.NonNull;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
import android.media.AudioSystem;
import android.util.Log;
@@ -86,12 +86,12 @@ public class AudioSystemAdapter {
}
/**
- * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDeviceAddress)}
+ * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDevice)}
* @param strategy
* @param device
* @return
*/
- public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAddress device) {
+ public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDevice device) {
return AudioSystem.setPreferredDeviceForStrategy(strategy, device);
}
@@ -138,7 +138,7 @@ public class AudioSystemAdapter {
}
@Override
- public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAddress device) {
+ public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDevice device) {
return AudioSystem.AUDIO_STATUS_OK;
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 0d88388742d2..0f549842a763 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -99,6 +99,33 @@ public class AuthService extends SystemService {
public String[] getConfiguration(Context context) {
return context.getResources().getStringArray(R.array.config_biometric_sensors);
}
+
+ /**
+ * Allows us to mock FingerprintService for testing
+ */
+ @VisibleForTesting
+ public IFingerprintService getFingerprintService() {
+ return IFingerprintService.Stub.asInterface(
+ ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+ }
+
+ /**
+ * Allows us to mock FaceService for testing
+ */
+ @VisibleForTesting
+ public IFaceService getFaceService() {
+ return IFaceService.Stub.asInterface(
+ ServiceManager.getService(Context.FACE_SERVICE));
+ }
+
+ /**
+ * Allows us to mock IrisService for testing
+ */
+ @VisibleForTesting
+ public IIrisService getIrisService() {
+ return IIrisService.Stub.asInterface(
+ ServiceManager.getService(Context.IRIS_SERVICE));
+ }
}
private final class AuthServiceImpl extends IAuthService.Stub {
@@ -178,7 +205,6 @@ public class AuthService extends SystemService {
mInjector = injector;
mImpl = new AuthServiceImpl();
- final PackageManager pm = context.getPackageManager();
}
private void registerAuthenticator(SensorConfig config) throws RemoteException {
@@ -191,18 +217,36 @@ public class AuthService extends SystemService {
switch (config.mModality) {
case TYPE_FINGERPRINT:
- authenticator = new FingerprintAuthenticator(IFingerprintService.Stub.asInterface(
- ServiceManager.getService(Context.FINGERPRINT_SERVICE)));
+ final IFingerprintService fingerprintService = mInjector.getFingerprintService();
+ if (fingerprintService == null) {
+ Slog.e(TAG, "Attempting to register with null FingerprintService. Please check"
+ + " your device configuration.");
+ return;
+ }
+
+ authenticator = new FingerprintAuthenticator(fingerprintService);
break;
case TYPE_FACE:
- authenticator = new FaceAuthenticator(IFaceService.Stub.asInterface(
- ServiceManager.getService(Context.FACE_SERVICE)));
+ final IFaceService faceService = mInjector.getFaceService();
+ if (faceService == null) {
+ Slog.e(TAG, "Attempting to register with null FaceService. Please check your"
+ + " device configuration.");
+ return;
+ }
+
+ authenticator = new FaceAuthenticator(faceService);
break;
case TYPE_IRIS:
- authenticator = new IrisAuthenticator(IIrisService.Stub.asInterface(
- ServiceManager.getService(Context.IRIS_SERVICE)));
+ final IIrisService irisService = mInjector.getIrisService();
+ if (irisService == null) {
+ Slog.e(TAG, "Attempting to register with null IrisService. Please check your"
+ + " device configuration.");
+ return;
+ }
+
+ authenticator = new IrisAuthenticator(irisService);
break;
default:
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index 2992f1e47dc7..a7e1a2876f81 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -291,7 +291,7 @@ final class WifiDisplayController implements DumpUtils.Dump {
mWfdEnabling = true;
WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();
- wfdInfo.setWfdEnabled(true);
+ wfdInfo.setEnabled(true);
wfdInfo.setDeviceType(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE);
wfdInfo.setSessionAvailable(true);
wfdInfo.setControlPort(DEFAULT_CONTROL_PORT);
@@ -323,7 +323,7 @@ final class WifiDisplayController implements DumpUtils.Dump {
// WFD should be disabled.
if (mWfdEnabled || mWfdEnabling) {
WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();
- wfdInfo.setWfdEnabled(false);
+ wfdInfo.setEnabled(false);
mWifiP2pManager.setWfdInfo(mWifiP2pChannel, wfdInfo, new ActionListener() {
@Override
public void onSuccess() {
@@ -1044,7 +1044,7 @@ final class WifiDisplayController implements DumpUtils.Dump {
private static boolean isWifiDisplay(WifiP2pDevice device) {
WifiP2pWfdInfo wfdInfo = device.getWfdInfo();
return wfdInfo != null
- && wfdInfo.isWfdEnabled()
+ && wfdInfo.isEnabled()
&& isPrimarySinkDeviceType(wfdInfo.getDeviceType());
}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 9754b6d4db02..0450647b9403 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -39,13 +39,20 @@ import android.content.integrity.Rule;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.content.pm.parsing.ApkParseUtils;
+import android.content.pm.parsing.PackageInfoUtils;
+import android.content.pm.parsing.ParsedPackage;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
@@ -67,6 +74,7 @@ import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -121,11 +129,11 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
IntegrityFileManager.getInstance(),
handlerThread.getThreadHandler(),
Settings.Global.getInt(
- context.getContentResolver(),
- Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
- 0)
- == 1
- );
+ context.getContentResolver(),
+ Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
+ 0)
+ == 1
+ );
}
@VisibleForTesting
@@ -260,16 +268,25 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
return;
}
- String appCert = getCertificateFingerprint(packageInfo);
+ List<String> appCertificates = getCertificateFingerprint(packageInfo);
+ List<String> installerCertificates =
+ getInstallerCertificateFingerprint(installerPackageName);
+
+ // TODO (b/148373316): Figure out what field contains which fields are populated for
+ // rotated and the multiple signers. Until then, return the first certificate.
+ String appCert = appCertificates.isEmpty() ? "" : appCertificates.get(0);
+ String installerCert =
+ installerCertificates.isEmpty() ? "" : installerCertificates.get(0);
+
+ Slog.w(TAG, appCertificates.toString());
AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
builder.setPackageName(getPackageNameNormalized(packageName));
- builder.setAppCertificate(appCert == null ? "" : appCert);
+ builder.setAppCertificate(appCert);
builder.setVersionCode(intent.getLongExtra(EXTRA_LONG_VERSION_CODE, -1));
builder.setInstallerName(getPackageNameNormalized(installerPackageName));
- builder.setInstallerCertificate(
- getInstallerCertificateFingerprint(installerPackageName));
+ builder.setInstallerCertificate(installerCert);
builder.setIsPreInstalled(isSystemApp(packageName));
AppInstallMetadata appInstallMetadata = builder.build();
@@ -320,7 +337,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
* Verify the UID and return the installer package name.
*
* @return the package name of the installer, or null if it cannot be determined or it is
- * installed via adb.
+ * installed via adb.
*/
@Nullable
private String getInstallerPackageName(Intent intent) {
@@ -399,23 +416,27 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
}
}
- private String getCertificateFingerprint(@NonNull PackageInfo packageInfo) {
- return getFingerprint(getSignature(packageInfo));
- }
-
- private String getInstallerCertificateFingerprint(String installer) {
+ private List<String> getInstallerCertificateFingerprint(String installer) {
if (installer.equals(ADB_INSTALLER) || installer.equals(UNKNOWN_INSTALLER)) {
- return INSTALLER_CERT_NOT_APPLICABLE;
+ return Collections.emptyList();
}
try {
PackageInfo installerInfo =
mContext.getPackageManager()
- .getPackageInfo(installer, PackageManager.GET_SIGNATURES);
+ .getPackageInfo(installer, PackageManager.GET_SIGNING_CERTIFICATES);
return getCertificateFingerprint(installerInfo);
} catch (PackageManager.NameNotFoundException e) {
Slog.i(TAG, "Installer package " + installer + " not found.");
- return "";
+ return Collections.emptyList();
+ }
+ }
+
+ private List<String> getCertificateFingerprint(@NonNull PackageInfo packageInfo) {
+ ArrayList<String> certificateFingerprints = new ArrayList();
+ for (Signature signature : getSignatures(packageInfo)) {
+ certificateFingerprints.add(getFingerprint(signature));
}
+ return certificateFingerprints;
}
/** Get the allowed installers and their associated certificate hashes from <meta-data> tag. */
@@ -445,12 +466,15 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
return packageCertMap;
}
- private static Signature getSignature(@NonNull PackageInfo packageInfo) {
- if (packageInfo.signatures == null || packageInfo.signatures.length < 1) {
+ private static Signature[] getSignatures(@NonNull PackageInfo packageInfo) {
+ SigningInfo signingInfo = packageInfo.signingInfo;
+
+ if (signingInfo == null || signingInfo.getApkContentsSigners().length < 1) {
throw new IllegalArgumentException("Package signature not found in " + packageInfo);
}
- // Only the first element is guaranteed to be present.
- return packageInfo.signatures[0];
+
+ // We are only interested in evaluating the active signatures.
+ return signingInfo.getApkContentsSigners();
}
private static String getFingerprint(Signature cert) {
@@ -489,20 +513,14 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
if (installationPath == null) {
throw new IllegalArgumentException("Installation path is null, package not found");
}
- PackageInfo packageInfo;
+
+ PackageParser parser = new PackageParser();
try {
- // The installation path will be a directory for a multi-apk install on L+
- if (installationPath.isDirectory()) {
- packageInfo = getMultiApkInfo(installationPath);
- } else {
- packageInfo =
- mContext.getPackageManager()
- .getPackageArchiveInfo(
- installationPath.getPath(),
- PackageManager.GET_SIGNATURES
- | PackageManager.GET_META_DATA);
- }
- return packageInfo;
+ ParsedPackage pkg = parser.parseParsedPackage(installationPath, 0, false);
+ int flags = PackageManager.GET_SIGNING_CERTIFICATES | PackageManager.GET_META_DATA;
+ ApkParseUtils.collectCertificates(pkg, false);
+ return PackageInfoUtils.generate(pkg, null, flags, 0, 0, null, new PackageUserState(),
+ UserHandle.getCallingUserId());
} catch (Exception e) {
throw new IllegalArgumentException("Exception reading " + dataUri, e);
}
@@ -515,7 +533,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
mContext.getPackageManager()
.getPackageArchiveInfo(
baseFile.getAbsolutePath(),
- PackageManager.GET_SIGNATURES | PackageManager.GET_META_DATA);
+ PackageManager.GET_SIGNING_CERTIFICATES
+ | PackageManager.GET_META_DATA);
if (basePackageInfo == null) {
for (File apkFile : multiApkDirectory.listFiles()) {
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 669f1ac6750c..647430399bc9 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -27,6 +27,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.media.AudioManager;
import android.media.MediaRoute2Info;
import android.text.TextUtils;
import android.util.Slog;
@@ -55,6 +56,7 @@ class BluetoothRouteProvider {
private final Context mContext;
private final BluetoothAdapter mBluetoothAdapter;
private final BluetoothRoutesUpdatedListener mListener;
+ private final AudioManager mAudioManager;
private final Map<String, BluetoothEventReceiver> mEventReceiverMap = new HashMap<>();
private final IntentFilter mIntentFilter = new IntentFilter();
private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
@@ -82,6 +84,7 @@ class BluetoothRouteProvider {
mContext = context;
mBluetoothAdapter = btAdapter;
mListener = listener;
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
buildBluetoothRoutes();
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
@@ -181,12 +184,15 @@ class BluetoothRouteProvider {
private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo();
newBtRoute.btDevice = device;
+ // Current / Max volume will be set when connected.
+ // TODO: Is there any BT device which has fixed volume?
newBtRoute.route = new MediaRoute2Info.Builder(device.getAddress(), device.getName())
.addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
.setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
.setDescription(mContext.getResources().getText(
R.string.bluetooth_a2dp_audio_route_name).toString())
.setDeviceType(MediaRoute2Info.DEVICE_TYPE_BLUETOOTH)
+ .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
.build();
newBtRoute.connectedProfiles = new SparseBooleanArray();
return newBtRoute;
@@ -203,10 +209,20 @@ class BluetoothRouteProvider {
Slog.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null");
return;
}
- if (btRoute.route.getConnectionState() != state) {
- btRoute.route = new MediaRoute2Info.Builder(btRoute.route)
- .setConnectionState(state).build();
+ if (btRoute.route.getConnectionState() == state) {
+ return;
+ }
+
+ // Update volume when the connection state is changed.
+ MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(btRoute.route)
+ .setConnectionState(state);
+
+ if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) {
+ int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ int currentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+ builder.setVolumeMax(maxVolume).setVolume(currentVolume);
}
+ btRoute.route = builder.build();
}
interface BluetoothRoutesUpdatedListener {
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 888f7ce74ba4..b5dcea8bb51e 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -20,9 +20,11 @@ import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.AudioRoutesInfo;
import android.media.IAudioRoutesObserver;
@@ -107,6 +109,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
});
initializeSessionInfo();
+
+ mContext.registerReceiver(new VolumeChangeReceiver(),
+ new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
}
@Override
@@ -278,4 +283,44 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
mCallback.onSessionUpdated(this, sessionInfo);
}
+
+ private class VolumeChangeReceiver extends BroadcastReceiver {
+ // This will be called in the main thread.
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
+ return;
+ }
+
+ final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+ if (streamType != AudioManager.STREAM_MUSIC) {
+ return;
+ }
+
+ final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
+ final int oldVolume = intent.getIntExtra(
+ AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
+
+ if (newVolume != oldVolume) {
+ String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+ if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
+ for (int i = mBluetoothRoutes.size() - 1; i >= 0; i--) {
+ MediaRoute2Info route = mBluetoothRoutes.get(i);
+ if (TextUtils.equals(activeBtDeviceAddress, route.getId())) {
+ mBluetoothRoutes.set(i,
+ new MediaRoute2Info.Builder(route)
+ .setVolume(newVolume)
+ .build());
+ break;
+ }
+ }
+ } else {
+ mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute)
+ .setVolume(newVolume)
+ .build();
+ }
+ publishRoutes();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f07113591fa5..46dc21bbeaad 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -9256,7 +9256,7 @@ public class NotificationManagerService extends SystemService {
}
BackgroundThread.getHandler().post(() -> {
- if (hasCompanionDevice(serviceInfo)) {
+ if (serviceInfo.isSystem || hasCompanionDevice(serviceInfo)) {
notifyNotificationChannelChanged(
serviceInfo, pkg, user, channel, modificationType);
}
@@ -9276,7 +9276,7 @@ public class NotificationManagerService extends SystemService {
}
BackgroundThread.getHandler().post(() -> {
- if (hasCompanionDevice(serviceInfo)) {
+ if (serviceInfo.isSystem || hasCompanionDevice(serviceInfo)) {
notifyNotificationChannelGroupChanged(
serviceInfo, pkg, user, group, modificationType);
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 42bc464e943f..a440c62a5f3c 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -36,6 +36,7 @@ import android.content.pm.parsing.AndroidPackage;
import android.os.Environment;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.Trace;
import android.sysprop.ApexProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -46,6 +47,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.utils.TimingsTraceAndSlog;
import com.google.android.collect.Lists;
@@ -375,8 +377,11 @@ public abstract class ApexManager {
@Override
public List<ActiveApexInfo> getActiveApexInfos() {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
+ Trace.TRACE_TAG_APEX_MANAGER);
synchronized (mLock) {
if (mActiveApexInfosCache == null) {
+ t.traceBegin("getActiveApexInfos_noCache");
try {
mActiveApexInfosCache = new ArraySet<>();
final ApexInfo[] activePackages = mApexService.getActivePackages();
@@ -387,6 +392,7 @@ public abstract class ApexManager {
} catch (RemoteException e) {
Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
}
+ t.traceEnd();
}
if (mActiveApexInfosCache != null) {
return new ArrayList<>(mActiveApexInfosCache);
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index e550bae7484b..482fc4944691 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -344,18 +344,10 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
if (extractLibs) {
- if (onIncremental) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER,
- "incrementalNativeBinaries");
- abi32 = NativeLibraryHelper.configureNativeBinariesForSupportedAbi(pkg,
- handle, nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
- useIsaSpecificSubdirs);
- } else {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
- abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
- useIsaSpecificSubdirs);
- }
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+ abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
+ useIsaSpecificSubdirs, onIncremental);
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
abi32 = NativeLibraryHelper.findSupportedAbi(
@@ -375,18 +367,10 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
if (extractLibs) {
- if (onIncremental) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER,
- "incrementalNativeBinaries");
- abi64 = NativeLibraryHelper.configureNativeBinariesForSupportedAbi(pkg,
- handle, nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
- useIsaSpecificSubdirs);
- } else {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
- abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
- useIsaSpecificSubdirs);
- }
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+ abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
+ useIsaSpecificSubdirs, onIncremental);
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
abi64 = NativeLibraryHelper.findSupportedAbi(
@@ -437,15 +421,9 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
final int copyRet;
if (extractLibs) {
- if (onIncremental) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "incrementalNativeBinaries");
- copyRet = NativeLibraryHelper.configureNativeBinariesForSupportedAbi(pkg,
- handle, nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
- } else {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
- copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
- }
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+ copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, abiList, useIsaSpecificSubdirs, onIncremental);
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 9116c4032ad5..33ef2d43d720 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -129,6 +129,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
/** Upper bound on number of historical sessions for a UID */
private static final long MAX_HISTORICAL_SESSIONS = 1048576;
+ /**
+ * Allow verification-skipping if it's a development app installed through ADB with
+ * disable verification flag specified.
+ */
+ private static final int ADB_DEV_MODE = PackageManager.INSTALL_FROM_ADB
+ | PackageManager.INSTALL_ALLOW_TEST;
+
private final Context mContext;
private final PackageManagerService mPm;
private final ApexManager mApexManager;
@@ -531,8 +538,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
params.installFlags &= ~PackageManager.INSTALL_REQUEST_DOWNGRADE;
}
- if (callingUid != Process.SYSTEM_UID) {
- // Only system_server can use INSTALL_DISABLE_VERIFICATION.
+ if (callingUid != Process.SYSTEM_UID
+ && (params.installFlags & ADB_DEV_MODE) != ADB_DEV_MODE) {
+ // Only system_server or tools under specific conditions (test app installed
+ // through ADB, and verification disabled flag specified) can disable verification.
params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0cf8b424b84d..944280d6db88 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1681,10 +1681,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mInternalProgress = 0.5f;
computeProgressLocked(true);
- // Unpack native libraries for non-incremental installation
- if (!isIncrementalInstallation()) {
- extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
- }
+ extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
}
// We've reached point of no return; call into PMS to install the stage.
@@ -2260,7 +2257,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
}
- private static void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)
+ private void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)
throws PackageManagerException {
final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
if (!inherit) {
@@ -2272,7 +2269,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
handle = NativeLibraryHelper.Handle.create(packageDir);
final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
- abiOverride);
+ abiOverride, isIncrementalInstallation());
if (res != PackageManager.INSTALL_SUCCEEDED) {
throw new PackageManagerException(res,
"Failed to extract native libraries, res=" + res);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c85859072d89..88c048cea267 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3627,7 +3627,7 @@ public class PackageManagerService extends IPackageManager.Stub
try {
handle = NativeLibraryHelper.Handle.create(dstCodePath);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
- null /*abiOverride*/);
+ null /*abiOverride*/, false /*isIncremental*/);
} catch (IOException e) {
logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
+ "; pkg: " + packageName);
@@ -13350,42 +13350,53 @@ public class PackageManagerService extends IPackageManager.Stub
*
* @return true if verification should be performed
*/
- private boolean isVerificationEnabled(int userId, int installFlags, int installerUid) {
+ private boolean isVerificationEnabled(
+ PackageInfoLite pkgInfoLite, int userId, int installFlags, int installerUid) {
if (!DEFAULT_VERIFY_ENABLE) {
return false;
}
- if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
- return false;
- }
-
// Check if installing from ADB
if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
if (isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
return true;
}
- // Check if the developer does not want package verification for ADB installs
+ // Check if the developer wants to skip verification for ADB installs
+ if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
+ synchronized (mLock) {
+ if (mSettings.mPackages.get(pkgInfoLite.packageName) == null) {
+ // Always verify fresh install
+ return true;
+ }
+ }
+ // Only skip when apk is debuggable
+ return !pkgInfoLite.debuggable;
+ }
return Global.getInt(mContext.getContentResolver(),
Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
- } else {
- // only when not installed from ADB, skip verification for instant apps when
- // the installer and verifier are the same.
- if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
- if (mInstantAppInstallerActivity != null
- && mInstantAppInstallerActivity.packageName.equals(
- mRequiredVerifierPackage)) {
- try {
- mInjector.getAppOpsManager()
- .checkPackage(installerUid, mRequiredVerifierPackage);
- if (DEBUG_VERIFY) {
- Slog.i(TAG, "disable verification for instant app");
- }
- return false;
- } catch (SecurityException ignore) { }
- }
+ }
+
+ if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
+ return false;
+ }
+
+ // only when not installed from ADB, skip verification for instant apps when
+ // the installer and verifier are the same.
+ if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
+ if (mInstantAppInstallerActivity != null
+ && mInstantAppInstallerActivity.packageName.equals(
+ mRequiredVerifierPackage)) {
+ try {
+ mInjector.getAppOpsManager()
+ .checkPackage(installerUid, mRequiredVerifierPackage);
+ if (DEBUG_VERIFY) {
+ Slog.i(TAG, "disable verification for instant app");
+ }
+ return false;
+ } catch (SecurityException ignore) { }
}
- return true;
}
+ return true;
}
/**
@@ -14549,7 +14560,7 @@ public class PackageManagerService extends IPackageManager.Stub
verificationInfo == null ? -1 : verificationInfo.installerUid;
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(
- verifierUser.getIdentifier(), installFlags, installerUid)) {
+ pkgLite, verifierUser.getIdentifier(), installFlags, installerUid)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -14959,12 +14970,13 @@ public class PackageManagerService extends IPackageManager.Stub
return ret;
}
+ final boolean isIncremental = isIncrementalPath(codeFile.getAbsolutePath());
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
- abiOverride);
+ abiOverride, isIncremental);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 71a5545c4131..9395c972f04a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -794,6 +794,7 @@ public class PackageManagerServiceUtils {
ret.verifiers = pkg.verifiers;
ret.recommendedInstallLocation = recommendedInstallLocation;
ret.multiArch = pkg.multiArch;
+ ret.debuggable = pkg.debuggable;
return ret;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index bb69680fb9f9..cb9404397f3d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2757,6 +2757,9 @@ class PackageManagerShellCommand extends ShellCommand {
case "--no-wait":
params.mWaitForStagedSessionReady = false;
break;
+ case "--skip-verification":
+ sessionParams.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
+ break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 89354537526c..614cc3fc2f3a 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -57,6 +57,7 @@ import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
+import android.text.TextUtils;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -67,6 +68,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageHelper;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
+import com.android.server.rollback.WatchdogRollbackLogger;
import java.io.File;
import java.io.IOException;
@@ -99,6 +101,10 @@ public class StagingManager {
@GuardedBy("mStagedSessions")
private final SparseIntArray mSessionRollbackIds = new SparseIntArray();
+ @GuardedBy("mFailedPackageNames")
+ private final List<String> mFailedPackageNames = new ArrayList<>();
+ private String mNativeFailureReason;
+
StagingManager(PackageInstallerService pi, Context context) {
mPi = pi;
mContext = context;
@@ -441,6 +447,22 @@ public class StagingManager {
}
}
+ /**
+ * Prepares for the logging of apexd reverts by storing the native failure reason if necessary,
+ * and adding the package name of the session which apexd reverted to the list of reverted
+ * session package names.
+ * Logging needs to wait until the ACTION_BOOT_COMPLETED broadcast is sent.
+ */
+ private void prepareForLoggingApexdRevert(@NonNull PackageInstallerSession session,
+ @NonNull String nativeFailureReason) {
+ synchronized (mFailedPackageNames) {
+ mNativeFailureReason = nativeFailureReason;
+ if (session.getPackageName() != null) {
+ mFailedPackageNames.add(session.getPackageName());
+ }
+ }
+ }
+
private void resumeSession(@NonNull PackageInstallerSession session) {
Slog.d(TAG, "Resuming session " + session.sessionId);
@@ -450,6 +472,12 @@ public class StagingManager {
// Check with apexservice whether the apex packages have been activated.
apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId);
+ // Prepare for logging a native crash during boot, if one occurred.
+ if (apexSessionInfo != null && !TextUtils.isEmpty(
+ apexSessionInfo.crashingNativeProcess)) {
+ prepareForLoggingApexdRevert(session, apexSessionInfo.crashingNativeProcess);
+ }
+
if (apexSessionInfo != null && apexSessionInfo.isVerified) {
// Session has been previously submitted to apexd, but didn't complete all the
// pre-reboot verification, perhaps because the device rebooted in the meantime.
@@ -955,12 +983,23 @@ public class StagingManager {
}
}
+ private void logFailedApexSessionsIfNecessary() {
+ synchronized (mFailedPackageNames) {
+ if (!mFailedPackageNames.isEmpty()) {
+ WatchdogRollbackLogger.logApexdRevert(mContext,
+ mFailedPackageNames, mNativeFailureReason);
+ }
+ }
+ }
+
void systemReady() {
// Register the receiver of boot completed intent for staging manager.
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context ctx, Intent intent) {
mPreRebootVerificationHandler.readyToStart();
+ BackgroundThread.getExecutor().execute(
+ () -> logFailedApexSessionsIfNecessary());
ctx.unregisterReceiver(this);
}
}, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0cb8f49540be..1c02161c5b96 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1901,7 +1901,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public boolean hasBaseUserRestriction(String restrictionKey, @UserIdInt int userId) {
- checkManageUsersPermission("hasBaseUserRestriction");
+ checkManageOrCreateUsersPermission("hasBaseUserRestriction");
if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) {
return false;
}
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 4d7af9cc0d44..b5da1c2ec58a 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -182,6 +182,15 @@ class Rollback {
private int mNumPackageSessionsWithSuccess;
/**
+ * A temp flag to facilitate merging of the 2 rollback collections managed by
+ * RollbackManagerServiceImpl. True if this rollback is in the process of enabling and was
+ * originally managed by RollbackManagerServiceImpl#mNewRollbacks.
+ * TODO: remove this flag when merge is completed.
+ */
+ @GuardedBy("mLock")
+ private boolean mIsNewRollback = false;
+
+ /**
* Constructs a new, empty Rollback instance.
*
* @param rollbackId the id of the rollback.
@@ -829,6 +838,18 @@ class Rollback {
}
}
+ void setIsNewRollback(boolean newRollback) {
+ synchronized (mLock) {
+ mIsNewRollback = newRollback;
+ }
+ }
+
+ boolean isNewRollback() {
+ synchronized (mLock) {
+ return mIsNewRollback;
+ }
+ }
+
static String rollbackStateToString(@RollbackState int state) {
switch (state) {
case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 8bd9533727d6..1421258c12f6 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -19,6 +19,7 @@ package com.android.server.rollback;
import android.Manifest;
import android.annotation.AnyThread;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.AppOpsManager;
@@ -50,7 +51,6 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
-import android.util.ArraySet;
import android.util.IntArray;
import android.util.Log;
import android.util.LongArrayQueue;
@@ -121,10 +121,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
@GuardedBy("mLock")
private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();
- // Rollbacks we are in the process of enabling.
- @GuardedBy("mLock")
- private final Set<Rollback> mNewRollbacks = new ArraySet<>();
-
// The list of all rollbacks, including available and committed rollbacks.
@GuardedBy("mLock")
private final List<Rollback> mRollbacks;
@@ -240,17 +236,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK token=" + token);
}
synchronized (mLock) {
- Rollback found = null;
- for (Rollback newRollback : mNewRollbacks) {
- if (newRollback.hasToken(token)) {
- found = newRollback;
+ for (int i = 0; i < mRollbacks.size(); ++i) {
+ Rollback rollback = mRollbacks.get(i);
+ if (rollback.hasToken(token) && rollback.isEnabling()) {
+ mRollbacks.remove(i);
+ rollback.delete(mAppDataRollbackHelper);
break;
}
}
- if (found != null) {
- mNewRollbacks.remove(found);
- found.delete(mAppDataRollbackHelper);
- }
}
}
}
@@ -442,15 +435,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
rollback.delete(mAppDataRollbackHelper);
}
}
- Iterator<Rollback> iter2 = mNewRollbacks.iterator();
- while (iter2.hasNext()) {
- Rollback newRollback = iter2.next();
- if (newRollback.includesPackage(packageName)) {
- iter2.remove();
- newRollback.delete(mAppDataRollbackHelper);
- }
-
- }
}
}
@@ -810,7 +794,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
newRollback = getNewRollbackForPackageSessionLocked(packageSession.getSessionId());
if (newRollback == null) {
newRollback = createNewRollbackLocked(parentSession);
- mNewRollbacks.add(newRollback);
+ mRollbacks.add(newRollback);
+ newRollback.setIsNewRollback(true);
}
}
newRollback.addToken(token);
@@ -818,34 +803,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
return enableRollbackForPackageSession(newRollback, packageSession);
}
- @WorkerThread
- private void removeRollbackForPackageSessionId(int sessionId) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "removeRollbackForPackageSessionId=" + sessionId);
- }
-
- synchronized (mLock) {
- Rollback newRollback = getNewRollbackForPackageSessionLocked(sessionId);
- if (newRollback != null) {
- Slog.w(TAG, "Delete new rollback id=" + newRollback.info.getRollbackId()
- + " for session id=" + sessionId);
- mNewRollbacks.remove(newRollback);
- newRollback.delete(mAppDataRollbackHelper);
- }
- Iterator<Rollback> iter = mRollbacks.iterator();
- while (iter.hasNext()) {
- Rollback rollback = iter.next();
- if (rollback.getStagedSessionId() == sessionId) {
- Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
- + " for session id=" + sessionId);
- iter.remove();
- rollback.delete(mAppDataRollbackHelper);
- break;
- }
- }
- }
- }
-
/**
* Do code and userdata backups to enable rollback of the given session.
* In case of multiPackage sessions, <code>session</code> should be one of
@@ -966,15 +923,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
+ " users=" + Arrays.toString(userIds));
}
synchronized (mLock) {
- // staged installs
for (int i = 0; i < mRollbacks.size(); i++) {
Rollback rollback = mRollbacks.get(i);
rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
}
- // non-staged installs
- for (Rollback rollback : mNewRollbacks) {
- rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
- }
}
}
@@ -1200,7 +1152,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
synchronized (mLock) {
newRollback = getNewRollbackForPackageSessionLocked(sessionId);
if (newRollback != null && newRollback.notifySessionWithSuccess()) {
- mNewRollbacks.remove(newRollback);
+ mRollbacks.remove(newRollback);
+ newRollback.setIsNewRollback(false);
} else {
// Not all child sessions finished with success.
// Don't enable the rollback yet.
@@ -1215,7 +1168,15 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
}
} else {
- removeRollbackForPackageSessionId(sessionId);
+ synchronized (mLock) {
+ Rollback rollback = getRollbackForSessionLocked(sessionId);
+ if (rollback != null && rollback.isEnabling()) {
+ Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
+ + " for failed session id=" + sessionId);
+ mRollbacks.remove(rollback);
+ rollback.delete(mAppDataRollbackHelper);
+ }
+ }
}
}
}
@@ -1376,6 +1337,25 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
/**
+ * Returns the Rollback associated with the given session if parent or child session id matches.
+ * Returns null if not found.
+ */
+ @WorkerThread
+ @GuardedBy("mLock")
+ @Nullable
+ private Rollback getRollbackForSessionLocked(int sessionId) {
+ // We expect mRollbacks to be a very small list; linear search should be plenty fast.
+ for (int i = 0; i < mRollbacks.size(); ++i) {
+ Rollback rollback = mRollbacks.get(i);
+ if (rollback.getStagedSessionId() == sessionId
+ || rollback.containsSessionId(sessionId)) {
+ return rollback;
+ }
+ }
+ return null;
+ }
+
+ /**
* Returns the NewRollback associated with the given package session.
* Returns null if no NewRollback is found for the given package
* session.
@@ -1383,11 +1363,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
@WorkerThread
@GuardedBy("mLock")
Rollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
- // We expect mNewRollbacks to be a very small list; linear search
+ // We expect mRollbacks to be a very small list; linear search
// should be plenty fast.
- for (Rollback newRollback: mNewRollbacks) {
- if (newRollback.containsSessionId(packageSessionId)) {
- return newRollback;
+ for (Rollback rollback: mRollbacks) {
+ if (rollback.isNewRollback() && rollback.containsSessionId(packageSessionId)) {
+ return rollback;
}
}
return null;
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index 46ec2f8258ca..f3f14a95eac6 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -20,7 +20,12 @@ import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCU
import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK;
import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT;
import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -36,7 +41,6 @@ import android.util.Slog;
import android.util.StatsLog;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.PackageWatchdog;
import java.util.ArrayList;
@@ -58,8 +62,8 @@ public final class WatchdogRollbackLogger {
private static String getLoggingParentName(Context context, @NonNull String packageName) {
PackageManager packageManager = context.getPackageManager();
try {
- ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
- PackageManager.GET_META_DATA);
+ int flags = PackageManager.MATCH_APEX | PackageManager.GET_META_DATA;
+ ApplicationInfo ai = packageManager.getPackageInfo(packageName, flags).applicationInfo;
if (ai.metaData == null) {
return null;
}
@@ -95,6 +99,22 @@ public final class WatchdogRollbackLogger {
return loggingParent;
}
+
+ /**
+ * Gets the set of parent packages for a given set of failed package names. In the case that
+ * multiple sessions have failed, we want to log failure for each of the parent packages.
+ * Even if multiple failed packages have the same parent, we only log the parent package once.
+ */
+ private static Set<VersionedPackage> getLogPackages(Context context,
+ @NonNull List<String> failedPackageNames) {
+ Set<VersionedPackage> parentPackages = new ArraySet<>();
+ for (String failedPackageName: failedPackageNames) {
+ parentPackages.add(getLogPackage(context, new VersionedPackage(failedPackageName, 0)));
+ }
+ return parentPackages;
+ }
+
+
static void logRollbackStatusOnBoot(Context context, int rollbackId,
List<RollbackInfo> recentlyCommittedRollbacks) {
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
@@ -142,19 +162,36 @@ public final class WatchdogRollbackLogger {
for (VersionedPackage oldLoggingPackage : oldLoggingPackages) {
if (sessionInfo.isStagedSessionApplied()) {
logEvent(oldLoggingPackage,
- FrameworkStatsLog
- .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+ WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
} else if (sessionInfo.isStagedSessionFailed()) {
logEvent(oldLoggingPackage,
- FrameworkStatsLog
- .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+ WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
}
}
}
/**
+ * Logs that one or more apexd reverts have occurred, along with the crashing native process
+ * that caused apexd to revert during boot.
+ *
+ * @param context the context to use when determining the log packages
+ * @param failedPackageNames a list of names of packages which were reverted
+ * @param failingNativeProcess the crashing native process which caused a revert
+ */
+ public static void logApexdRevert(Context context, @NonNull List<String> failedPackageNames,
+ @NonNull String failingNativeProcess) {
+ Set<VersionedPackage> logPackages = getLogPackages(context, failedPackageNames);
+ for (VersionedPackage logPackage: logPackages) {
+ logEvent(logPackage,
+ WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+ WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT,
+ failingNativeProcess);
+ }
+ }
+
+ /**
* Log a Watchdog rollback event to statsd.
*
* @param logPackage the package to associate the rollback with.
@@ -196,14 +233,13 @@ public final class WatchdogRollbackLogger {
private static String rollbackTypeToString(int type) {
switch (type) {
- case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
return "ROLLBACK_INITIATE";
- case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
return "ROLLBACK_SUCCESS";
- case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE:
return "ROLLBACK_FAILURE";
- case FrameworkStatsLog
- .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED:
return "ROLLBACK_BOOT_TRIGGERED";
default:
return "UNKNOWN";
@@ -212,16 +248,16 @@ public final class WatchdogRollbackLogger {
private static String rollbackReasonToString(int reason) {
switch (reason) {
- case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH:
return "REASON_NATIVE_CRASH";
- case FrameworkStatsLog
- .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK:
return "REASON_EXPLICIT_HEALTH_CHECK";
- case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH:
return "REASON_APP_CRASH";
- case FrameworkStatsLog
- .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING:
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING:
return "REASON_APP_NOT_RESPONDING";
+ case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT:
+ return "REASON_NATIVE_CRASH_DURING_BOOT";
default:
return "UNKNOWN";
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 0418afaf033a..798665972a33 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -240,6 +240,10 @@ class InsetsSourceProvider {
target = target.getWindow().getImeControlTarget();
}
+ if (mWin != null && mWin.getSurfaceControl() == null) {
+ // if window doesn't have a surface, set it null and return.
+ setWindow(null, null, null);
+ }
if (mWin == null) {
mControlTarget = target;
return;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 87c91ef6b96c..b1db9d7889dd 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -79,11 +79,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
-import static com.android.server.wm.TaskProto.FILLS_PARENT;
-import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
-import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
-import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
@@ -125,7 +120,6 @@ import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.util.DisplayMetrics;
import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.ITaskOrganizer;
import android.view.RemoteAnimationTarget;
@@ -3195,12 +3189,16 @@ class Task extends WindowContainer<WindowContainer> {
info.lastActiveTime = lastActiveTime;
info.taskDescription = new ActivityManager.TaskDescription(getTaskDescription());
info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
- info.resizeMode = mResizeMode;
info.configuration.setTo(getConfiguration());
info.token = mRemoteToken;
// Get's the first non-undefined activity type among this and children. Can't use
// configuration.windowConfiguration because that would only be this level.
info.topActivityType = getActivityType();
+
+ //TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child
+ // order changes.
+ final Task top = getTopMostTask();
+ info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 096541f57ba5..0a0530c92a16 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -23,6 +23,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
@@ -47,6 +49,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@@ -375,6 +378,45 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
}
}
+ @Override
+ public List<RunningTaskInfo> getChildTasks(IWindowContainer parent) {
+ enforceStackPermission("getChildTasks()");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ if (parent == null) {
+ throw new IllegalArgumentException("Can't get children of null parent");
+ }
+ final WindowContainer container = WindowContainer.fromBinder(parent.asBinder());
+ if (container == null) {
+ Slog.e(TAG, "Can't get children of " + parent + " because it is not valid.");
+ return null;
+ }
+ // For now, only support returning children of persistent root tasks (of which the
+ // only current implementation is TaskTile).
+ if (!(container instanceof TaskTile)) {
+ Slog.w(TAG, "Can only get children of root tasks created via createRootTask");
+ return null;
+ }
+ ArrayList<RunningTaskInfo> out = new ArrayList<>();
+ // Tiles aren't real parents, so we need to go through stacks on the display to
+ // ensure correct ordering.
+ final DisplayContent dc = container.getDisplayContent();
+ for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack as = dc.getStackAt(i);
+ if (as.getTile() == container) {
+ final RunningTaskInfo info = new RunningTaskInfo();
+ as.fillTaskInfo(info);
+ out.add(info);
+ }
+ }
+ return out;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private int sanitizeAndApplyChange(WindowContainer container,
WindowContainerTransaction.Change change) {
if (!(container instanceof Task)) {
@@ -405,6 +447,54 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
return effects;
}
+ private int sanitizeAndApplyHierarchyOp(WindowContainer container,
+ WindowContainerTransaction.HierarchyOp hop) {
+ if (!(container instanceof Task)) {
+ throw new IllegalArgumentException("Invalid container in hierarchy op");
+ }
+ if (hop.isReparent()) {
+ // special case for tiles since they are "virtual" parents
+ if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
+ ActivityStack as = (ActivityStack) container;
+ TaskTile newParent = hop.getNewParent() == null ? null
+ : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
+ if (as.getTile() != newParent) {
+ if (as.getTile() != null) {
+ as.getTile().removeChild(as);
+ }
+ if (newParent != null) {
+ if (!as.affectedBySplitScreenResize()) {
+ return 0;
+ }
+ newParent.addChild(as, POSITION_TOP);
+ }
+ }
+ if (hop.getToTop()) {
+ as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+ } else {
+ as.getDisplay().positionStackAtBottom(as);
+ }
+ } else if (container instanceof Task) {
+ throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
+ }
+ } else {
+ // Ugh, of course ActivityStack has its own special reorder logic...
+ if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
+ ActivityStack as = (ActivityStack) container;
+ if (hop.getToTop()) {
+ as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+ } else {
+ as.getDisplay().positionStackAtBottom(as);
+ }
+ } else {
+ container.getParent().positionChildAt(
+ hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+ container, false /* includingParents */);
+ }
+ }
+ return TRANSACT_EFFECTS_LIFECYCLE;
+ }
+
private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
int windowMask, Configuration config) {
if ((container instanceof ActivityStack)
@@ -470,8 +560,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
while (entries.hasNext()) {
final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
entries.next();
- final WindowContainer wc = WindowContainer.RemoteToken.fromBinder(
- entry.getKey()).getContainer();
+ final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
int containerEffect = applyWindowContainerChange(wc, entry.getValue());
effects |= containerEffect;
@@ -484,6 +573,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
mBLASTSyncEngine.addToSyncSet(syncId, wc);
}
}
+ // Hierarchy changes
+ final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
+ for (int i = 0, n = hops.size(); i < n; ++i) {
+ final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
+ final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+ effects |= sanitizeAndApplyHierarchyOp(wc, hop);
+ }
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
// Already calls ensureActivityConfig
mService.mRootWindowContainer.ensureActivitiesVisible(
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 9acb660967cb..504aa2dc7b4c 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2296,6 +2296,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return mRemoteToken;
}
+ static WindowContainer fromBinder(IBinder binder) {
+ return RemoteToken.fromBinder(binder).getContainer();
+ }
+
static class RemoteToken extends IWindowContainer.Stub {
final WeakReference<WindowContainer> mWeakRef;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ec6b6c43215b..633098566b1b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5687,6 +5687,12 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void setForceShowSystemBars(boolean show) {
+ boolean isAutomotive = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
+ if (!isAutomotive) {
+ throw new UnsupportedOperationException("Force showing system bars is only supported"
+ + "for Automotive use cases.");
+ }
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller does not hold permission "
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 87b04b2ae68f..c7d00ec5daab 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -178,7 +178,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
private long mLastActivityFinishTime;
// Last configuration that was reported to the process.
- private final Configuration mLastReportedConfiguration;
+ private final Configuration mLastReportedConfiguration = new Configuration();
// Configuration that is waiting to be dispatched to the process.
private Configuration mPendingConfiguration;
private final Configuration mNewOverrideConfig = new Configuration();
@@ -192,7 +192,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
/** Whether our process is currently running a {@link IRemoteAnimationRunner} */
private boolean mRunningRemoteAnimation;
- public WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info,
+ public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
String name, int uid, int userId, Object owner, WindowProcessListener listener) {
mInfo = info;
mName = name;
@@ -201,11 +201,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mOwner = owner;
mListener = listener;
mAtm = atm;
- mLastReportedConfiguration = new Configuration();
mDisplayId = INVALID_DISPLAY;
- if (atm != null) {
- onConfigurationChanged(atm.getGlobalConfiguration());
- }
+ onConfigurationChanged(atm.getGlobalConfiguration());
}
public void setPid(int pid) {
@@ -220,6 +217,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
public void setThread(IApplicationThread thread) {
synchronized (mAtm.mGlobalLockWithoutBoost) {
mThread = thread;
+ // In general this is called from attaching application, so the last configuration
+ // has been sent to client by {@link android.app.IApplicationThread#bindApplication}.
+ // If this process is system server, it is fine because system is booting and a new
+ // configuration will update when display is ready.
+ setLastReportedConfiguration(getConfiguration());
}
}
@@ -1060,7 +1062,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mNewOverrideConfig.setTo(mergedOverrideConfig);
mNewOverrideConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
super.onRequestedOverrideConfigurationChanged(mNewOverrideConfig);
- updateConfiguration();
}
private void updateConfiguration() {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 390068e1fa75..812bc438246f 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -109,6 +109,7 @@ cc_defaults {
"libinputservice",
"libprotobuf-cpp-lite",
"libprotoutil",
+ "libstatshidl",
"libstatspull",
"libstatssocket",
"libstatslog",
@@ -155,6 +156,7 @@ cc_defaults {
"android.hardware.vr@1.0",
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
+ "android.frameworks.stats@1.0",
"android.system.suspend@1.0",
"service.incremental",
"suspend_control_aidl_interface-cpp",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 67254b811ee0..279ea4b9a790 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -29,6 +29,7 @@
#include <schedulerservice/SchedulingPolicyService.h>
#include <sensorservice/SensorService.h>
#include <sensorservicehidl/SensorManager.h>
+#include <stats/StatsHal.h>
#include <bionic/malloc.h>
#include <bionic/reserved_signals.h>
@@ -59,6 +60,8 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /
using ::android::frameworks::schedulerservice::V1_0::implementation::SchedulingPolicyService;
using ::android::frameworks::sensorservice::V1_0::ISensorManager;
using ::android::frameworks::sensorservice::V1_0::implementation::SensorManager;
+ using ::android::frameworks::stats::V1_0::IStats;
+ using ::android::frameworks::stats::V1_0::implementation::StatsHal;
using ::android::hardware::configureRpcThreadpool;
status_t err;
@@ -75,6 +78,10 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /
sp<ISchedulingPolicyService> schedulingService = new SchedulingPolicyService();
err = schedulingService->registerAsService();
ALOGE_IF(err != OK, "Cannot register %s: %d", ISchedulingPolicyService::descriptor, err);
+
+ sp<IStats> statsHal = new StatsHal();
+ err = statsHal->registerAsService();
+ ALOGE_IF(err != OK, "Cannot register %s: %d", IStats::descriptor, err);
}
static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */,
diff --git a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
index d825b6b2bd8f..45e0aac24ca7 100644
--- a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
@@ -107,7 +107,7 @@ class CallLogQueryHelper {
}
@Event.EventType int eventType = CALL_TYPE_TO_EVENT_TYPE.get(callType);
Event event = new Event.Builder(date, eventType)
- .setCallDetails(new Event.CallDetails(durationSeconds))
+ .setDurationSeconds((int) durationSeconds)
.build();
mEventConsumer.accept(phoneNumber, event);
return true;
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index bb97533b3222..b60ed3e8783f 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -35,7 +35,7 @@ import java.util.Objects;
*/
public class ConversationInfo {
- private static final int FLAG_VIP = 1;
+ private static final int FLAG_IMPORTANT = 1;
private static final int FLAG_NOTIFICATION_SILENCED = 1 << 1;
@@ -50,7 +50,7 @@ public class ConversationInfo {
private static final int FLAG_DEMOTED = 1 << 6;
@IntDef(flag = true, prefix = {"FLAG_"}, value = {
- FLAG_VIP,
+ FLAG_IMPORTANT,
FLAG_NOTIFICATION_SILENCED,
FLAG_BUBBLED,
FLAG_PERSON_IMPORTANT,
@@ -129,9 +129,9 @@ public class ConversationInfo {
return hasShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED);
}
- /** Whether this conversation is marked as VIP by the user. */
- public boolean isVip() {
- return hasConversationFlags(FLAG_VIP);
+ /** Whether this conversation is marked as important by the user. */
+ public boolean isImportant() {
+ return hasConversationFlags(FLAG_IMPORTANT);
}
/** Whether the notifications for this conversation should be silenced. */
@@ -208,8 +208,8 @@ public class ConversationInfo {
sb.append("]");
sb.append(", conversationFlags=0x").append(Integer.toHexString(mConversationFlags));
sb.append(" [");
- if (isVip()) {
- sb.append("Vip");
+ if (isImportant()) {
+ sb.append("Imp");
}
if (isNotificationSilenced()) {
sb.append("Sil");
@@ -221,7 +221,7 @@ public class ConversationInfo {
sb.append("Dem");
}
if (isPersonImportant()) {
- sb.append("Imp");
+ sb.append("PIm");
}
if (isPersonBot()) {
sb.append("Bot");
@@ -318,8 +318,8 @@ public class ConversationInfo {
return this;
}
- Builder setVip(boolean value) {
- return setConversationFlag(FLAG_VIP, value);
+ Builder setImportant(boolean value) {
+ return setConversationFlag(FLAG_IMPORTANT, value);
}
Builder setNotificationSilenced(boolean value) {
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index f17e1b91cb5d..364992181f75 100644
--- a/services/people/java/com/android/server/people/data/ConversationStore.java
+++ b/services/people/java/com/android/server/people/data/ConversationStore.java
@@ -40,6 +40,9 @@ class ConversationStore {
// Phone Number -> Shortcut ID
private final Map<String, String> mPhoneNumberToShortcutIdMap = new ArrayMap<>();
+ // Notification Channel ID -> Shortcut ID
+ private final Map<String, String> mNotifChannelIdToShortcutIdMap = new ArrayMap<>();
+
void addOrUpdate(@NonNull ConversationInfo conversationInfo) {
mConversationInfoMap.put(conversationInfo.getShortcutId(), conversationInfo);
@@ -57,6 +60,11 @@ class ConversationStore {
if (phoneNumber != null) {
mPhoneNumberToShortcutIdMap.put(phoneNumber, conversationInfo.getShortcutId());
}
+
+ String notifChannelId = conversationInfo.getNotificationChannelId();
+ if (notifChannelId != null) {
+ mNotifChannelIdToShortcutIdMap.put(notifChannelId, conversationInfo.getShortcutId());
+ }
}
void deleteConversation(@NonNull String shortcutId) {
@@ -79,6 +87,11 @@ class ConversationStore {
if (phoneNumber != null) {
mPhoneNumberToShortcutIdMap.remove(phoneNumber);
}
+
+ String notifChannelId = conversationInfo.getNotificationChannelId();
+ if (notifChannelId != null) {
+ mNotifChannelIdToShortcutIdMap.remove(notifChannelId);
+ }
}
void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) {
@@ -106,4 +119,9 @@ class ConversationStore {
ConversationInfo getConversationByPhoneNumber(@NonNull String phoneNumber) {
return getConversation(mPhoneNumberToShortcutIdMap.get(phoneNumber));
}
+
+ @Nullable
+ ConversationInfo getConversationByNotificationChannelId(@NonNull String notifChannelId) {
+ return getConversation(mNotifChannelIdToShortcutIdMap.get(notifChannelId));
+ }
}
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 43e773870124..7a3ed5348d30 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -21,11 +21,11 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.app.Person;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
-import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -69,6 +69,7 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* A class manages the lifecycle of the conversations and associated data, and exposes the methods
@@ -96,7 +97,6 @@ public class DataManager {
private final ContentObserver mMmsSmsContentObserver;
private ShortcutServiceInternal mShortcutServiceInternal;
- private UsageStatsManagerInternal mUsageStatsManagerInternal;
private ShortcutManager mShortcutManager;
private UserManager mUserManager;
@@ -118,7 +118,6 @@ public class DataManager {
/** Initialization. Called when the system services are up running. */
public void initialize() {
mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
- mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
mShortcutManager = mContext.getSystemService(ShortcutManager.class);
mUserManager = mContext.getSystemService(UserManager.class);
@@ -160,8 +159,8 @@ public class DataManager {
mNotificationListeners.put(userId, notificationListener);
try {
notificationListener.registerAsSystemService(mContext,
- new ComponentName(PLATFORM_PACKAGE_NAME, getClass().getSimpleName()),
- UserHandle.myUserId());
+ new ComponentName(PLATFORM_PACKAGE_NAME, getClass().getCanonicalName()),
+ userId);
} catch (RemoteException e) {
// Should never occur for local calls.
}
@@ -386,36 +385,6 @@ public class DataManager {
}
@VisibleForTesting
- @WorkerThread
- void queryUsageStatsService(@UserIdInt int userId, long currentTime, long lastQueryTime) {
- UsageEvents usageEvents = mUsageStatsManagerInternal.queryEventsForUser(
- userId, lastQueryTime, currentTime, false, false);
- if (usageEvents == null) {
- return;
- }
- while (usageEvents.hasNextEvent()) {
- UsageEvents.Event e = new UsageEvents.Event();
- usageEvents.getNextEvent(e);
-
- String packageName = e.getPackageName();
- PackageData packageData = getPackage(packageName, userId);
- if (packageData == null) {
- continue;
- }
- if (e.getEventType() == UsageEvents.Event.SHORTCUT_INVOCATION) {
- String shortcutId = e.getShortcutId();
- if (packageData.getConversationStore().getConversation(shortcutId) != null) {
- EventHistoryImpl eventHistory =
- packageData.getEventStore().getOrCreateShortcutEventHistory(
- shortcutId);
- eventHistory.addEvent(
- new Event(e.getTimeStamp(), Event.TYPE_SHORTCUT_INVOCATION));
- }
- }
- }
- }
-
- @VisibleForTesting
ContentObserver getContactsContentObserverForTesting(@UserIdInt int userId) {
return mContactsContentObservers.get(userId);
}
@@ -604,6 +573,44 @@ public class DataManager {
long currentTime = System.currentTimeMillis();
eventHistory.addEvent(new Event(currentTime, Event.TYPE_NOTIFICATION_OPENED));
}
+
+ @Override
+ public void onNotificationChannelModified(String pkg, UserHandle user,
+ NotificationChannel channel, int modificationType) {
+ PackageData packageData = getPackage(pkg, user.getIdentifier());
+ String shortcutId = channel.getConversationId();
+ if (packageData == null || shortcutId == null) {
+ return;
+ }
+ ConversationStore conversationStore = packageData.getConversationStore();
+ ConversationInfo conversationInfo = conversationStore.getConversation(shortcutId);
+ if (conversationInfo == null) {
+ return;
+ }
+ ConversationInfo.Builder builder = new ConversationInfo.Builder(conversationInfo);
+ switch (modificationType) {
+ case NOTIFICATION_CHANNEL_OR_GROUP_ADDED:
+ case NOTIFICATION_CHANNEL_OR_GROUP_UPDATED:
+ builder.setNotificationChannelId(channel.getId());
+ builder.setImportant(channel.isImportantConversation());
+ builder.setDemoted(channel.isDemoted());
+ builder.setNotificationSilenced(
+ channel.getImportance() <= NotificationManager.IMPORTANCE_LOW);
+ builder.setBubbled(channel.canBubble());
+ break;
+ case NOTIFICATION_CHANNEL_OR_GROUP_DELETED:
+ // If the notification channel is deleted, revert all the notification settings
+ // to the default value.
+ builder.setNotificationChannelId(null);
+ builder.setImportant(false);
+ builder.setDemoted(false);
+ builder.setNotificationSilenced(false);
+ builder.setBubbled(false);
+ break;
+ }
+ conversationStore.addOrUpdate(builder.build());
+ // TODO: Cache the shortcut when a conversation's notification setting is changed.
+ }
}
/**
@@ -612,19 +619,20 @@ public class DataManager {
*/
private class UsageStatsQueryRunnable implements Runnable {
- private final int mUserId;
- private long mLastQueryTime;
+ private final UsageStatsQueryHelper mUsageStatsQueryHelper;
+ private long mLastEventTimestamp;
private UsageStatsQueryRunnable(int userId) {
- mUserId = userId;
- mLastQueryTime = System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS;
+ mUsageStatsQueryHelper = mInjector.createUsageStatsQueryHelper(userId,
+ (packageName) -> getPackage(packageName, userId));
+ mLastEventTimestamp = System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS;
}
@Override
public void run() {
- long currentTime = System.currentTimeMillis();
- queryUsageStatsService(mUserId, currentTime, mLastQueryTime);
- mLastQueryTime = currentTime;
+ if (mUsageStatsQueryHelper.querySince(mLastEventTimestamp)) {
+ mLastEventTimestamp = mUsageStatsQueryHelper.getLastEventTimestamp();
+ }
}
}
@@ -680,6 +688,11 @@ public class DataManager {
return new SmsQueryHelper(context, eventConsumer);
}
+ UsageStatsQueryHelper createUsageStatsQueryHelper(@UserIdInt int userId,
+ Function<String, PackageData> packageDataGetter) {
+ return new UsageStatsQueryHelper(userId, packageDataGetter);
+ }
+
int getCallingUserId() {
return Binder.getCallingUserHandle().getIdentifier();
}
diff --git a/services/people/java/com/android/server/people/data/Event.java b/services/people/java/com/android/server/people/data/Event.java
index c2364a295e30..81411c00db51 100644
--- a/services/people/java/com/android/server/people/data/Event.java
+++ b/services/people/java/com/android/server/people/data/Event.java
@@ -18,14 +18,12 @@ package com.android.server.people.data;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.text.format.DateFormat;
import android.util.ArraySet;
-import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
import java.util.Set;
/** An event representing the interaction with a specific conversation or app. */
@@ -55,6 +53,8 @@ public class Event {
public static final int TYPE_CALL_MISSED = 12;
+ public static final int TYPE_IN_APP_CONVERSATION = 13;
+
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_SHORTCUT_INVOCATION,
TYPE_NOTIFICATION_POSTED,
@@ -68,6 +68,7 @@ public class Event {
TYPE_CALL_OUTGOING,
TYPE_CALL_INCOMING,
TYPE_CALL_MISSED,
+ TYPE_IN_APP_CONVERSATION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventType {}
@@ -95,6 +96,7 @@ public class Event {
CALL_EVENT_TYPES.add(TYPE_CALL_MISSED);
ALL_EVENT_TYPES.add(TYPE_SHORTCUT_INVOCATION);
+ ALL_EVENT_TYPES.add(TYPE_IN_APP_CONVERSATION);
ALL_EVENT_TYPES.addAll(NOTIFICATION_EVENT_TYPES);
ALL_EVENT_TYPES.addAll(SHARE_EVENT_TYPES);
ALL_EVENT_TYPES.addAll(SMS_EVENT_TYPES);
@@ -105,18 +107,18 @@ public class Event {
private final int mType;
- private final CallDetails mCallDetails;
+ private final int mDurationSeconds;
Event(long timestamp, @EventType int type) {
mTimestamp = timestamp;
mType = type;
- mCallDetails = null;
+ mDurationSeconds = 0;
}
private Event(@NonNull Builder builder) {
mTimestamp = builder.mTimestamp;
mType = builder.mType;
- mCallDetails = builder.mCallDetails;
+ mDurationSeconds = builder.mDurationSeconds;
}
public long getTimestamp() {
@@ -128,12 +130,35 @@ public class Event {
}
/**
- * Gets the {@link CallDetails} of the event. It is only available if the event type is one of
- * {@code CALL_EVENT_TYPES}, otherwise, it's always {@code null}.
+ * Gets the duration of the event in seconds. It is only available for these events:
+ * <ul>
+ * <li>{@link #TYPE_CALL_INCOMING}
+ * <li>{@link #TYPE_CALL_OUTGOING}
+ * <li>{@link #TYPE_IN_APP_CONVERSATION}
+ * </ul>
+ * <p>For the other event types, it always returns {@code 0}.
*/
- @Nullable
- public CallDetails getCallDetails() {
- return mCallDetails;
+ public int getDurationSeconds() {
+ return mDurationSeconds;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Event)) {
+ return false;
+ }
+ Event other = (Event) obj;
+ return mTimestamp == other.mTimestamp
+ && mType == other.mType
+ && mDurationSeconds == other.mDurationSeconds;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTimestamp, mType, mDurationSeconds);
}
@Override
@@ -142,32 +167,13 @@ public class Event {
sb.append("Event {");
sb.append("timestamp=").append(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimestamp));
sb.append(", type=").append(mType);
- if (mCallDetails != null) {
- sb.append(", callDetails=").append(mCallDetails);
+ if (mDurationSeconds > 0) {
+ sb.append(", durationSeconds=").append(mDurationSeconds);
}
sb.append("}");
return sb.toString();
}
- /** Type-specific details of a call event. */
- public static class CallDetails {
-
- private final long mDurationSeconds;
-
- CallDetails(long durationSeconds) {
- mDurationSeconds = durationSeconds;
- }
-
- public long getDurationSeconds() {
- return mDurationSeconds;
- }
-
- @Override
- public String toString() {
- return "CallDetails {durationSeconds=" + mDurationSeconds + "}";
- }
- }
-
/** Builder class for {@link Event} objects. */
static class Builder {
@@ -175,16 +181,15 @@ public class Event {
private final int mType;
- private CallDetails mCallDetails;
+ private int mDurationSeconds;
Builder(long timestamp, @EventType int type) {
mTimestamp = timestamp;
mType = type;
}
- Builder setCallDetails(CallDetails callDetails) {
- Preconditions.checkArgument(CALL_EVENT_TYPES.contains(mType));
- mCallDetails = callDetails;
+ Builder setDurationSeconds(int durationSeconds) {
+ mDurationSeconds = durationSeconds;
return this;
}
diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
new file mode 100644
index 000000000000..4e37f47149b4
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
@@ -0,0 +1,158 @@
+/*
+ * 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.people.data;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.LocusId;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
+
+import com.android.server.LocalServices;
+
+import java.util.Map;
+import java.util.function.Function;
+
+/** A helper class that queries {@link UsageStatsManagerInternal}. */
+class UsageStatsQueryHelper {
+
+ private final UsageStatsManagerInternal mUsageStatsManagerInternal;
+ private final int mUserId;
+ private final Function<String, PackageData> mPackageDataGetter;
+ // Activity name -> Conversation start event (LOCUS_ID_SET)
+ private final Map<ComponentName, UsageEvents.Event> mConvoStartEvents = new ArrayMap<>();
+ private long mLastEventTimestamp;
+
+ /**
+ * @param userId The user whose events are to be queried.
+ * @param packageDataGetter The function to get {@link PackageData} with a package name.
+ */
+ UsageStatsQueryHelper(@UserIdInt int userId,
+ Function<String, PackageData> packageDataGetter) {
+ mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
+ mUserId = userId;
+ mPackageDataGetter = packageDataGetter;
+ }
+
+ /**
+ * Queries {@link UsageStatsManagerInternal} for the recent events occurred since {@code
+ * sinceTime} and adds the derived {@link Event}s into the corresponding package's event store,
+ *
+ * @return true if the query runs successfully and at least one event is found.
+ */
+ boolean querySince(long sinceTime) {
+ UsageEvents usageEvents = mUsageStatsManagerInternal.queryEventsForUser(
+ mUserId, sinceTime, System.currentTimeMillis(), false, false);
+ if (usageEvents == null) {
+ return false;
+ }
+ boolean hasEvents = false;
+ while (usageEvents.hasNextEvent()) {
+ UsageEvents.Event e = new UsageEvents.Event();
+ usageEvents.getNextEvent(e);
+
+ hasEvents = true;
+ mLastEventTimestamp = Math.max(mLastEventTimestamp, e.getTimeStamp());
+ String packageName = e.getPackageName();
+ PackageData packageData = mPackageDataGetter.apply(packageName);
+ if (packageData == null) {
+ continue;
+ }
+ switch (e.getEventType()) {
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ addEventByShortcutId(packageData, e.getShortcutId(),
+ new Event(e.getTimeStamp(), Event.TYPE_SHORTCUT_INVOCATION));
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ addEventByNotificationChannelId(packageData, e.getNotificationChannelId(),
+ new Event(e.getTimeStamp(), Event.TYPE_NOTIFICATION_POSTED));
+ break;
+ case UsageEvents.Event.LOCUS_ID_SET:
+ onInAppConversationEnded(packageData, e);
+ LocusId locusId = e.getLocusId() != null ? new LocusId(e.getLocusId()) : null;
+ if (locusId != null) {
+ if (packageData.getConversationStore().getConversationByLocusId(locusId)
+ != null) {
+ ComponentName activityName =
+ new ComponentName(packageName, e.getClassName());
+ mConvoStartEvents.put(activityName, e);
+ }
+ }
+ break;
+ case UsageEvents.Event.ACTIVITY_PAUSED:
+ case UsageEvents.Event.ACTIVITY_STOPPED:
+ case UsageEvents.Event.ACTIVITY_DESTROYED:
+ onInAppConversationEnded(packageData, e);
+ break;
+ }
+ }
+ return hasEvents;
+ }
+
+ long getLastEventTimestamp() {
+ return mLastEventTimestamp;
+ }
+
+ private void onInAppConversationEnded(@NonNull PackageData packageData,
+ @NonNull UsageEvents.Event endEvent) {
+ ComponentName activityName =
+ new ComponentName(endEvent.getPackageName(), endEvent.getClassName());
+ UsageEvents.Event startEvent = mConvoStartEvents.remove(activityName);
+ if (startEvent == null || startEvent.getTimeStamp() >= endEvent.getTimeStamp()) {
+ return;
+ }
+ long durationMillis = endEvent.getTimeStamp() - startEvent.getTimeStamp();
+ Event event = new Event.Builder(startEvent.getTimeStamp(), Event.TYPE_IN_APP_CONVERSATION)
+ .setDurationSeconds((int) (durationMillis / DateUtils.SECOND_IN_MILLIS))
+ .build();
+ addEventByLocusId(packageData, new LocusId(startEvent.getLocusId()), event);
+ }
+
+ private void addEventByShortcutId(PackageData packageData, String shortcutId, Event event) {
+ if (packageData.getConversationStore().getConversation(shortcutId) == null) {
+ return;
+ }
+ EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory(
+ shortcutId);
+ eventHistory.addEvent(event);
+ }
+
+ private void addEventByLocusId(PackageData packageData, LocusId locusId, Event event) {
+ if (packageData.getConversationStore().getConversationByLocusId(locusId) == null) {
+ return;
+ }
+ EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateLocusEventHistory(
+ locusId);
+ eventHistory.addEvent(event);
+ }
+
+ private void addEventByNotificationChannelId(PackageData packageData,
+ String notificationChannelId, Event event) {
+ ConversationInfo conversationInfo =
+ packageData.getConversationStore().getConversationByNotificationChannelId(
+ notificationChannelId);
+ if (conversationInfo == null) {
+ return;
+ }
+ EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory(
+ conversationInfo.getShortcutId());
+ eventHistory.addEvent(event);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 8d2a152dba83..6083ce34a3bd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -174,12 +174,12 @@ public class ApplicationExitInfoTest {
final int app1ConnectiongGroup = 10;
final int app1UidUser2 = 1010123;
final int app1PidUser2 = 12347;
- final int app1Pss1 = 34567;
- final int app1Rss1 = 45678;
- final int app1Pss2 = 34568;
- final int app1Rss2 = 45679;
- final int app1Pss3 = 34569;
- final int app1Rss3 = 45680;
+ final long app1Pss1 = 34567;
+ final long app1Rss1 = 45678;
+ final long app1Pss2 = 34568;
+ final long app1Rss2 = 45679;
+ final long app1Pss3 = 34569;
+ final long app1Rss3 = 45680;
final String app1ProcessName = "com.android.test.stub1:process";
final String app1PackageName = "com.android.test.stub1";
@@ -344,8 +344,8 @@ public class ApplicationExitInfoTest {
// Case 4: Create a process from another package with kill from lmkd
final int app2UidUser2 = 1010234;
final int app2PidUser2 = 12348;
- final int app2Pss1 = 54321;
- final int app2Rss1 = 65432;
+ final long app2Pss1 = 54321;
+ final long app2Rss1 = 65432;
final String app2ProcessName = "com.android.test.stub2:process";
final String app2PackageName = "com.android.test.stub2";
@@ -402,8 +402,8 @@ public class ApplicationExitInfoTest {
final int app3UidUser2 = 1010345;
final int app3PidUser2 = 12349;
final int app3ConnectiongGroup = 4;
- final int app3Pss1 = 54320;
- final int app3Rss1 = 65430;
+ final long app3Pss1 = 54320;
+ final long app3Rss1 = 65430;
final String app3ProcessName = "com.android.test.stub3:process";
final String app3PackageName = "com.android.test.stub3";
final String app3Description = "native crash";
@@ -529,8 +529,8 @@ public class ApplicationExitInfoTest {
final int app3Uid = 10345;
final int app3IsolatedUid = 99001; // it's an isolated process
final int app3Pid = 12350;
- final int app3Pss2 = 23232;
- final int app3Rss2 = 32323;
+ final long app3Pss2 = 23232;
+ final long app3Rss2 = 32323;
final String app3Description2 = "force close";
sleep(1);
@@ -618,8 +618,8 @@ public class ApplicationExitInfoTest {
sleep(1);
final int app1IsolatedUidUser2 = 1099002; // isolated uid
- final int app1Pss4 = 34343;
- final int app1Rss4 = 43434;
+ final long app1Pss4 = 34343;
+ final long app1Rss4 = 43434;
final long now8 = System.currentTimeMillis();
sigNum = OsConstants.SIGKILL;
doReturn(new Pair<Long, Object>(now8, makeSignalStatus(sigNum)))
@@ -673,8 +673,8 @@ public class ApplicationExitInfoTest {
sleep(1);
final int app1Pid2User2 = 56565;
final int app1IsolatedUid2User2 = 1099003; // isolated uid
- final int app1Pss5 = 34344;
- final int app1Rss5 = 43435;
+ final long app1Pss5 = 34344;
+ final long app1Rss5 = 43435;
final long now9 = System.currentTimeMillis();
sigNum = OsConstants.SIGKILL;
doReturn(new Pair<Long, Object>(now9, makeSignalStatus(sigNum)))
@@ -831,7 +831,7 @@ public class ApplicationExitInfoTest {
}
private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid,
- int connectionGroup, int procState, int pss, int rss,
+ int connectionGroup, int procState, long pss, long rss,
String processName, String packageName) {
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = packageName;
@@ -847,8 +847,8 @@ public class ApplicationExitInfoTest {
app.connectionGroup = connectionGroup;
app.setProcState = procState;
app.lastMemInfo = spy(new Debug.MemoryInfo());
- doReturn(pss).when(app.lastMemInfo).getTotalPss();
- doReturn(rss).when(app.lastMemInfo).getTotalRss();
+ doReturn((int) pss).when(app.lastMemInfo).getTotalPss();
+ doReturn((int) rss).when(app.lastMemInfo).getTotalRss();
return app;
}
@@ -856,7 +856,7 @@ public class ApplicationExitInfoTest {
Long timestamp, Integer pid, Integer uid, Integer packageUid,
Integer definingUid, String processName, Integer connectionGroup,
Integer reason, Integer subReason, Integer status,
- Integer pss, Integer rss, Integer importance, String description) {
+ Long pss, Long rss, Integer importance, String description) {
assertNotNull(info);
if (timestamp != null) {
@@ -892,10 +892,10 @@ public class ApplicationExitInfoTest {
assertEquals(status.intValue(), info.getStatus());
}
if (pss != null) {
- assertEquals(pss.intValue(), info.getPss());
+ assertEquals(pss.longValue(), info.getPss());
}
if (rss != null) {
- assertEquals(rss.intValue(), info.getRss());
+ assertEquals(rss.longValue(), info.getRss());
}
if (importance != null) {
assertEquals(importance.intValue(), info.getImportance());
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
index 16dde4203e91..25964c592872 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -43,6 +43,7 @@ import com.android.server.blob.BlobStoreManagerService.Injector;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -209,6 +210,7 @@ public class BlobStoreManagerServiceTest {
verify(file3, never()).delete();
}
+ @Ignore
@Test
public void testHandleIdleMaintenance_deleteStaleSessions() throws Exception {
// Setup sessions
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index cf10559c8198..dfe950ea93d6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -47,6 +47,7 @@ import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -703,14 +704,20 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void takeScreenshot_returnNull() {
- // no canTakeScreenshot, should return null.
- when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(false);
- assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue()));
-
// no checkAccessibilityAccess, should return null.
when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
- assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue()));
+ mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY, new RemoteCallback((result) -> {
+ assertNull(result);
+ }));
+ }
+
+ @Test (expected = SecurityException.class)
+ public void takeScreenshot_throwSecurityException() {
+ // no canTakeScreenshot, should throw security exception.
+ when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(false);
+ mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY, new RemoteCallback((result) -> {
+ }));
}
private void updateServiceInfo(AccessibilityServiceInfo serviceInfo, int eventType,
@@ -852,8 +859,5 @@ public class AbstractAccessibilityServiceConnectionTest {
@Override
public void onFingerprintGesture(int gesture) {}
-
- @Override
- public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index d38c80cdabe0..6aa928794244 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,6 +33,9 @@ import android.content.pm.PackageManager;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.face.IFaceService;
+import android.hardware.fingerprint.IFingerprintService;
+import android.hardware.iris.IIrisService;
import android.os.Binder;
import android.os.Bundle;
@@ -61,6 +65,12 @@ public class AuthServiceTest {
AuthService.Injector mInjector;
@Mock
IBiometricService mBiometricService;
+ @Mock
+ IFingerprintService mFingerprintService;
+ @Mock
+ IIrisService mIrisService;
+ @Mock
+ IFaceService mFaceService;
@Before
public void setUp() {
@@ -76,10 +86,28 @@ public class AuthServiceTest {
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mInjector.getBiometricService()).thenReturn(mBiometricService);
when(mInjector.getConfiguration(any())).thenReturn(config);
- when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
- .thenReturn(true);
- when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)).thenReturn(true);
- when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+ when(mInjector.getFingerprintService()).thenReturn(mFingerprintService);
+ when(mInjector.getFaceService()).thenReturn(mFaceService);
+ when(mInjector.getIrisService()).thenReturn(mIrisService);
+ }
+
+ @Test
+ public void testRegisterNullService_doesNotRegister() throws Exception {
+
+ // Config contains Fingerprint, Iris, Face, but services are all null
+
+ when(mInjector.getFingerprintService()).thenReturn(null);
+ when(mInjector.getFaceService()).thenReturn(null);
+ when(mInjector.getIrisService()).thenReturn(null);
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ verify(mBiometricService, never()).registerAuthenticator(
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ any());
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 4a7636a179b1..c9ec87427722 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -423,7 +423,8 @@ public class AppIntegrityManagerServiceImplTest {
PackageInfo packageInfo =
mRealContext
.getPackageManager()
- .getPackageInfo(TEST_FRAMEWORK_PACKAGE, PackageManager.GET_SIGNATURES);
+ .getPackageInfo(TEST_FRAMEWORK_PACKAGE,
+ PackageManager.GET_SIGNING_CERTIFICATES);
doReturn(packageInfo).when(mSpyPackageManager).getPackageInfo(eq(INSTALLER), anyInt());
doReturn(1).when(mSpyPackageManager).getPackageUid(eq(INSTALLER), anyInt());
return makeVerificationIntent(INSTALLER);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
index 7a16d171b475..a54501029712 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
@@ -92,7 +92,7 @@ public final class CallLogQueryHelperTest {
assertEquals(1, events.size());
assertEquals(Event.TYPE_CALL_INCOMING, events.get(0).getType());
assertEquals(100L, events.get(0).getTimestamp());
- assertEquals(30L, events.get(0).getCallDetails().getDurationSeconds());
+ assertEquals(30L, events.get(0).getDurationSeconds());
}
@Test
@@ -108,7 +108,7 @@ public final class CallLogQueryHelperTest {
assertEquals(1, events.size());
assertEquals(Event.TYPE_CALL_OUTGOING, events.get(0).getType());
assertEquals(100L, events.get(0).getTimestamp());
- assertEquals(40L, events.get(0).getCallDetails().getDurationSeconds());
+ assertEquals(40L, events.get(0).getDurationSeconds());
}
@Test
@@ -124,7 +124,7 @@ public final class CallLogQueryHelperTest {
assertEquals(1, events.size());
assertEquals(Event.TYPE_CALL_MISSED, events.get(0).getType());
assertEquals(100L, events.get(0).getTimestamp());
- assertEquals(0L, events.get(0).getCallDetails().getDurationSeconds());
+ assertEquals(0L, events.get(0).getDurationSeconds());
}
@Test
@@ -145,7 +145,7 @@ public final class CallLogQueryHelperTest {
assertEquals(100L, events.get(0).getTimestamp());
assertEquals(Event.TYPE_CALL_OUTGOING, events.get(1).getType());
assertEquals(110L, events.get(1).getTimestamp());
- assertEquals(40L, events.get(1).getCallDetails().getDurationSeconds());
+ assertEquals(40L, events.get(1).getDurationSeconds());
}
private class EventConsumer implements BiConsumer<String, Event> {
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
index 05a9a80e262c..c0e7927a8d72 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
@@ -47,7 +47,7 @@ public final class ConversationInfoTest {
.setContactPhoneNumber(PHONE_NUMBER)
.setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
.setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
- .setVip(true)
+ .setImportant(true)
.setNotificationSilenced(true)
.setBubbled(true)
.setDemoted(true)
@@ -62,7 +62,7 @@ public final class ConversationInfoTest {
assertEquals(PHONE_NUMBER, conversationInfo.getContactPhoneNumber());
assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
assertTrue(conversationInfo.isShortcutLongLived());
- assertTrue(conversationInfo.isVip());
+ assertTrue(conversationInfo.isImportant());
assertTrue(conversationInfo.isNotificationSilenced());
assertTrue(conversationInfo.isBubbled());
assertTrue(conversationInfo.isDemoted());
@@ -83,7 +83,7 @@ public final class ConversationInfoTest {
assertNull(conversationInfo.getContactPhoneNumber());
assertNull(conversationInfo.getNotificationChannelId());
assertFalse(conversationInfo.isShortcutLongLived());
- assertFalse(conversationInfo.isVip());
+ assertFalse(conversationInfo.isImportant());
assertFalse(conversationInfo.isNotificationSilenced());
assertFalse(conversationInfo.isBubbled());
assertFalse(conversationInfo.isDemoted());
@@ -101,7 +101,7 @@ public final class ConversationInfoTest {
.setContactPhoneNumber(PHONE_NUMBER)
.setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
.setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
- .setVip(true)
+ .setImportant(true)
.setNotificationSilenced(true)
.setBubbled(true)
.setPersonImportant(true)
@@ -110,7 +110,7 @@ public final class ConversationInfoTest {
.build();
ConversationInfo destination = new ConversationInfo.Builder(source)
- .setVip(false)
+ .setImportant(false)
.setContactStarred(false)
.build();
@@ -120,7 +120,7 @@ public final class ConversationInfoTest {
assertEquals(PHONE_NUMBER, destination.getContactPhoneNumber());
assertEquals(NOTIFICATION_CHANNEL_ID, destination.getNotificationChannelId());
assertTrue(destination.isShortcutLongLived());
- assertFalse(destination.isVip());
+ assertFalse(destination.isImportant());
assertTrue(destination.isNotificationSilenced());
assertTrue(destination.isBubbled());
assertTrue(destination.isPersonImportant());
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
index a40c6ab90197..331ad5972fc1 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
@@ -37,6 +37,7 @@ import java.util.Set;
public final class ConversationStoreTest {
private static final String SHORTCUT_ID = "abc";
+ private static final String NOTIFICATION_CHANNEL_ID = "test : abc";
private static final LocusId LOCUS_ID = new LocusId("def");
private static final Uri CONTACT_URI = Uri.parse("tel:+1234567890");
private static final String PHONE_NUMBER = "+1234567890";
@@ -59,16 +60,19 @@ public final class ConversationStoreTest {
@Test
public void testUpdateConversation() {
- ConversationInfo original =
- buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+ ConversationInfo original = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+ PHONE_NUMBER, null);
mConversationStore.addOrUpdate(original);
assertEquals(LOCUS_ID, mConversationStore.getConversation(SHORTCUT_ID).getLocusId());
+ assertNull(mConversationStore.getConversation(SHORTCUT_ID).getNotificationChannelId());
LocusId newLocusId = new LocusId("ghi");
ConversationInfo update = buildConversationInfo(
- SHORTCUT_ID, newLocusId, CONTACT_URI, PHONE_NUMBER);
+ SHORTCUT_ID, newLocusId, CONTACT_URI, PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
mConversationStore.addOrUpdate(update);
- assertEquals(newLocusId, mConversationStore.getConversation(SHORTCUT_ID).getLocusId());
+ ConversationInfo updated = mConversationStore.getConversation(SHORTCUT_ID);
+ assertEquals(newLocusId, updated.getLocusId());
+ assertEquals(NOTIFICATION_CHANNEL_ID, updated.getNotificationChannelId());
}
@Test
@@ -97,8 +101,8 @@ public final class ConversationStoreTest {
@Test
public void testGetConversationByLocusId() {
- ConversationInfo in =
- buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+ ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+ PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
mConversationStore.addOrUpdate(in);
ConversationInfo out = mConversationStore.getConversationByLocusId(LOCUS_ID);
assertNotNull(out);
@@ -110,8 +114,8 @@ public final class ConversationStoreTest {
@Test
public void testGetConversationByContactUri() {
- ConversationInfo in =
- buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+ ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+ PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
mConversationStore.addOrUpdate(in);
ConversationInfo out = mConversationStore.getConversationByContactUri(CONTACT_URI);
assertNotNull(out);
@@ -123,8 +127,8 @@ public final class ConversationStoreTest {
@Test
public void testGetConversationByPhoneNumber() {
- ConversationInfo in =
- buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+ ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+ PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
mConversationStore.addOrUpdate(in);
ConversationInfo out = mConversationStore.getConversationByPhoneNumber(PHONE_NUMBER);
assertNotNull(out);
@@ -134,19 +138,36 @@ public final class ConversationStoreTest {
assertNull(mConversationStore.getConversationByPhoneNumber(PHONE_NUMBER));
}
+ @Test
+ public void testGetConversationByNotificationChannelId() {
+ ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+ PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
+ mConversationStore.addOrUpdate(in);
+ ConversationInfo out = mConversationStore.getConversationByNotificationChannelId(
+ NOTIFICATION_CHANNEL_ID);
+ assertNotNull(out);
+ assertEquals(SHORTCUT_ID, out.getShortcutId());
+
+ mConversationStore.deleteConversation(SHORTCUT_ID);
+ assertNull(
+ mConversationStore.getConversationByNotificationChannelId(NOTIFICATION_CHANNEL_ID));
+ }
+
private static ConversationInfo buildConversationInfo(String shortcutId) {
- return buildConversationInfo(shortcutId, null, null, null);
+ return buildConversationInfo(shortcutId, null, null, null, null);
}
private static ConversationInfo buildConversationInfo(
- String shortcutId, LocusId locusId, Uri contactUri, String phoneNumber) {
+ String shortcutId, LocusId locusId, Uri contactUri, String phoneNumber,
+ String notificationChannelId) {
return new ConversationInfo.Builder()
.setShortcutId(shortcutId)
.setLocusId(locusId)
.setContactUri(contactUri)
.setContactPhoneNumber(phoneNumber)
+ .setNotificationChannelId(notificationChannelId)
.setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
- .setVip(true)
+ .setImportant(true)
.setBubbled(true)
.build();
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 9d2091a8578e..498d8886eec3 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -16,15 +16,17 @@
package com.android.server.people.data;
-import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -36,11 +38,12 @@ import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.app.Person;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
-import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ContentResolver;
import android.content.Context;
@@ -92,6 +95,7 @@ public final class DataManagerTest {
private static final String TEST_SHORTCUT_ID = "sc";
private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
private static final String PHONE_NUMBER = "+1234567890";
+ private static final String NOTIFICATION_CHANNEL_ID = "test : sc";
private static final long MILLIS_PER_MINUTE = 1000L * 60L;
@Mock private Context mContext;
@@ -107,6 +111,7 @@ public final class DataManagerTest {
@Mock private StatusBarNotification mStatusBarNotification;
@Mock private Notification mNotification;
+ private NotificationChannel mNotificationChannel;
private DataManager mDataManager;
private int mCallingUserId;
private TestInjector mInjector;
@@ -156,6 +161,10 @@ public final class DataManagerTest {
when(mStatusBarNotification.getUser()).thenReturn(UserHandle.of(USER_ID_PRIMARY));
when(mNotification.getShortcutId()).thenReturn(TEST_SHORTCUT_ID);
+ mNotificationChannel = new NotificationChannel(
+ NOTIFICATION_CHANNEL_ID, "test channel", NotificationManager.IMPORTANCE_DEFAULT);
+ mNotificationChannel.setConversationId("test", TEST_SHORTCUT_ID);
+
mCallingUserId = USER_ID_PRIMARY;
mInjector = new TestInjector();
@@ -288,9 +297,8 @@ public final class DataManagerTest {
}
@Test
- public void testNotificationListener() {
+ public void testNotificationOpened() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
- mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
@@ -312,32 +320,80 @@ public final class DataManagerTest {
}
@Test
- public void testQueryUsageStatsService() {
- UsageEvents.Event e = new UsageEvents.Event(SHORTCUT_INVOCATION,
- System.currentTimeMillis());
- e.mPackage = TEST_PKG_NAME;
- e.mShortcutId = TEST_SHORTCUT_ID;
- List<UsageEvents.Event> events = new ArrayList<>();
- events.add(e);
- UsageEvents usageEvents = new UsageEvents(events, new String[]{});
- when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(),
- anyBoolean(), anyBoolean())).thenReturn(usageEvents);
+ public void testNotificationChannelCreated() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ mDataManager.onUserUnlocked(USER_ID_SECONDARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+ mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
+
+ ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+ .getConversationStore()
+ .getConversation(TEST_SHORTCUT_ID);
+ assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
+ assertFalse(conversationInfo.isImportant());
+ assertFalse(conversationInfo.isNotificationSilenced());
+ assertFalse(conversationInfo.isDemoted());
+ }
+
+ @Test
+ public void testNotificationChannelModified() {
+ mNotificationChannel.setImportantConversation(true);
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.onShortcutAddedOrUpdated(shortcut);
- mDataManager.queryUsageStatsService(USER_ID_PRIMARY, 0L, Long.MAX_VALUE);
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+ mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+ ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+ .getConversationStore()
+ .getConversation(TEST_SHORTCUT_ID);
+ assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
+ assertTrue(conversationInfo.isImportant());
+ assertFalse(conversationInfo.isNotificationSilenced());
+ assertFalse(conversationInfo.isDemoted());
+ }
- List<Range<Long>> activeShortcutInvocationTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeShortcutInvocationTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.TYPE_SHORTCUT_INVOCATION)
- .getActiveTimeSlots()));
- assertEquals(1, activeShortcutInvocationTimeSlots.size());
+ @Test
+ public void testNotificationChannelDeleted() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ mDataManager.onUserUnlocked(USER_ID_SECONDARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+ mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
+ ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+ .getConversationStore()
+ .getConversation(TEST_SHORTCUT_ID);
+ assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
+
+ listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+ mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+ conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+ .getConversationStore()
+ .getConversation(TEST_SHORTCUT_ID);
+ assertNull(conversationInfo.getNotificationChannelId());
+ assertFalse(conversationInfo.isImportant());
+ assertFalse(conversationInfo.isNotificationSilenced());
+ assertFalse(conversationInfo.isDemoted());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
new file mode 100644
index 000000000000..e4248a04878d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
@@ -0,0 +1,295 @@
+/*
+ * 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.people.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.LocusId;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+
+@RunWith(JUnit4.class)
+public final class UsageStatsQueryHelperTest {
+
+ private static final int USER_ID_PRIMARY = 0;
+ private static final String PKG_NAME = "pkg";
+ private static final String ACTIVITY_NAME = "TestActivity";
+ private static final String SHORTCUT_ID = "abc";
+ private static final String NOTIFICATION_CHANNEL_ID = "test : abc";
+ private static final LocusId LOCUS_ID_1 = new LocusId("locus_1");
+ private static final LocusId LOCUS_ID_2 = new LocusId("locus_2");
+
+ @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
+
+ private TestPackageData mPackageData;
+ private UsageStatsQueryHelper mHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStatsManagerInternal);
+
+ mPackageData = new TestPackageData(PKG_NAME, USER_ID_PRIMARY, pkg -> false, pkg -> false);
+ mPackageData.mConversationStore.mConversationInfo = new ConversationInfo.Builder()
+ .setShortcutId(SHORTCUT_ID)
+ .setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
+ .setLocusId(LOCUS_ID_1)
+ .build();
+
+ mHelper = new UsageStatsQueryHelper(USER_ID_PRIMARY, pkg -> mPackageData);
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ }
+
+ @Test
+ public void testQueryNoEvents() {
+ assertFalse(mHelper.querySince(50L));
+ }
+
+ @Test
+ public void testQueryShortcutInvocationEvent() {
+ addUsageEvents(createShortcutInvocationEvent(100L));
+
+ assertTrue(mHelper.querySince(50L));
+ assertEquals(100L, mHelper.getLastEventTimestamp());
+ Event expectedEvent = new Event(100L, Event.TYPE_SHORTCUT_INVOCATION);
+ List<Event> events = mPackageData.mEventStore.mShortcutEventHistory.queryEvents(
+ Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(1, events.size());
+ assertEquals(expectedEvent, events.get(0));
+ }
+
+ @Test
+ public void testQueryNotificationInterruptionEvent() {
+ addUsageEvents(createNotificationInterruptionEvent(100L));
+
+ assertTrue(mHelper.querySince(50L));
+ assertEquals(100L, mHelper.getLastEventTimestamp());
+ Event expectedEvent = new Event(100L, Event.TYPE_NOTIFICATION_POSTED);
+ List<Event> events = mPackageData.mEventStore.mShortcutEventHistory.queryEvents(
+ Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(1, events.size());
+ assertEquals(expectedEvent, events.get(0));
+ }
+
+ @Test
+ public void testInAppConversationSwitch() {
+ addUsageEvents(
+ createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()),
+ createLocusIdSetEvent(110_000L, LOCUS_ID_2.getId()));
+
+ assertTrue(mHelper.querySince(50_000L));
+ assertEquals(110_000L, mHelper.getLastEventTimestamp());
+ List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents(
+ Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(1, events.size());
+ assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+ }
+
+ @Test
+ public void testInAppConversationExplicitlyEnd() {
+ addUsageEvents(
+ createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()),
+ createLocusIdSetEvent(110_000L, null));
+
+ assertTrue(mHelper.querySince(50_000L));
+ assertEquals(110_000L, mHelper.getLastEventTimestamp());
+ List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents(
+ Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(1, events.size());
+ assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+ }
+
+ @Test
+ public void testInAppConversationImplicitlyEnd() {
+ addUsageEvents(
+ createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()),
+ createActivityStoppedEvent(110_000L));
+
+ assertTrue(mHelper.querySince(50_000L));
+ assertEquals(110_000L, mHelper.getLastEventTimestamp());
+ List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents(
+ Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(1, events.size());
+ assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+ }
+
+ @Test
+ public void testMultipleInAppConversations() {
+ addUsageEvents(
+ createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()),
+ createLocusIdSetEvent(110_000L, LOCUS_ID_2.getId()),
+ createLocusIdSetEvent(130_000L, LOCUS_ID_1.getId()),
+ createActivityStoppedEvent(160_000L));
+
+ assertTrue(mHelper.querySince(50_000L));
+ assertEquals(160_000L, mHelper.getLastEventTimestamp());
+ List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents(
+ Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(3, events.size());
+ assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+ assertEquals(createInAppConversationEvent(110_000L, 20), events.get(1));
+ assertEquals(createInAppConversationEvent(130_000L, 30), events.get(2));
+ }
+
+ private void addUsageEvents(UsageEvents.Event ... events) {
+ UsageEvents usageEvents = new UsageEvents(Arrays.asList(events), new String[]{});
+ when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(),
+ anyBoolean(), anyBoolean())).thenReturn(usageEvents);
+ }
+
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
+ }
+
+ private static UsageEvents.Event createShortcutInvocationEvent(long timestamp) {
+ UsageEvents.Event e = createUsageEvent(UsageEvents.Event.SHORTCUT_INVOCATION, timestamp);
+ e.mShortcutId = SHORTCUT_ID;
+ return e;
+ }
+
+ private static UsageEvents.Event createNotificationInterruptionEvent(long timestamp) {
+ UsageEvents.Event e = createUsageEvent(UsageEvents.Event.NOTIFICATION_INTERRUPTION,
+ timestamp);
+ e.mNotificationChannelId = NOTIFICATION_CHANNEL_ID;
+ return e;
+ }
+
+ private static UsageEvents.Event createLocusIdSetEvent(long timestamp, String locusId) {
+ UsageEvents.Event e = createUsageEvent(UsageEvents.Event.LOCUS_ID_SET, timestamp);
+ e.mClass = ACTIVITY_NAME;
+ e.mLocusId = locusId;
+ return e;
+ }
+
+ private static UsageEvents.Event createActivityStoppedEvent(long timestamp) {
+ UsageEvents.Event e = createUsageEvent(UsageEvents.Event.ACTIVITY_STOPPED, timestamp);
+ e.mClass = ACTIVITY_NAME;
+ return e;
+ }
+
+ private static UsageEvents.Event createUsageEvent(int eventType, long timestamp) {
+ UsageEvents.Event e = new UsageEvents.Event(eventType, timestamp);
+ e.mPackage = PKG_NAME;
+ return e;
+ }
+
+ private static Event createInAppConversationEvent(long timestamp, int durationSeconds) {
+ return new Event.Builder(timestamp, Event.TYPE_IN_APP_CONVERSATION)
+ .setDurationSeconds(durationSeconds)
+ .build();
+ }
+
+ private static class TestConversationStore extends ConversationStore {
+
+ private ConversationInfo mConversationInfo;
+
+ @Override
+ @Nullable
+ ConversationInfo getConversation(@Nullable String shortcutId) {
+ return mConversationInfo;
+ }
+ }
+
+ private static class TestPackageData extends PackageData {
+
+ private final TestConversationStore mConversationStore = new TestConversationStore();
+ private final TestEventStore mEventStore = new TestEventStore();
+
+ TestPackageData(@NonNull String packageName, @UserIdInt int userId,
+ @NonNull Predicate<String> isDefaultDialerPredicate,
+ @NonNull Predicate<String> isDefaultSmsAppPredicate) {
+ super(packageName, userId, isDefaultDialerPredicate, isDefaultSmsAppPredicate);
+ }
+
+ @Override
+ @NonNull
+ ConversationStore getConversationStore() {
+ return mConversationStore;
+ }
+
+ @Override
+ @NonNull
+ EventStore getEventStore() {
+ return mEventStore;
+ }
+ }
+
+ private static class TestEventStore extends EventStore {
+
+ private final EventHistoryImpl mShortcutEventHistory = new TestEventHistoryImpl();
+ private final EventHistoryImpl mLocusEventHistory = new TestEventHistoryImpl();
+
+ @Override
+ @NonNull
+ EventHistoryImpl getOrCreateShortcutEventHistory(String shortcutId) {
+ return mShortcutEventHistory;
+ }
+
+ @Override
+ @NonNull
+ EventHistoryImpl getOrCreateLocusEventHistory(LocusId locusId) {
+ return mLocusEventHistory;
+ }
+ }
+
+ private static class TestEventHistoryImpl extends EventHistoryImpl {
+
+ private final List<Event> mEvents = new ArrayList<>();
+
+ @Override
+ @NonNull
+ public List<Event> queryEvents(Set<Integer> eventTypes, long startTime, long endTime) {
+ return mEvents;
+ }
+
+ @Override
+ void addEvent(Event event) {
+ mEvents.add(event);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
index ba493d4f9646..d1c9643859e3 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
@@ -20,7 +20,11 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -36,6 +40,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.List;
+
@RunWith(JUnit4.class)
public class WatchdogRollbackLoggerTest {
@@ -46,6 +52,11 @@ public class WatchdogRollbackLoggerTest {
private PackageInfo mPackageInfo;
private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT";
+ private static final String LOGGING_PARENT_VALUE = "logging.parent";
+ private static final int PACKAGE_INFO_FLAGS = PackageManager.MATCH_APEX
+ | PackageManager.GET_META_DATA;
+ private static final List<String> sFailingPackages =
+ List.of("package1", "package2", "package3");
@Before
public void setUp() {
@@ -64,10 +75,12 @@ public class WatchdogRollbackLoggerTest {
*/
@Test
public void testLogPackageHasNoMetadata() throws Exception {
- when(mMockPm.getApplicationInfo(anyString(), anyInt())).thenReturn(mApplicationInfo);
+ when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
sTestPackageV1);
assertThat(logPackage).isNull();
+ verify(mMockPm, times(1)).getPackageInfo(
+ sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
}
/**
@@ -76,12 +89,16 @@ public class WatchdogRollbackLoggerTest {
*/
@Test
public void testLogPackageParentKeyIsNull() throws Exception {
- when(mMockPm.getApplicationInfo(anyString(), anyInt())).thenReturn(mApplicationInfo);
+ when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
Bundle bundle = new Bundle();
bundle.putString(LOGGING_PARENT_KEY, null);
+ mApplicationInfo.metaData = bundle;
+ mPackageInfo.applicationInfo = mApplicationInfo;
VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
sTestPackageV1);
assertThat(logPackage).isNull();
+ verify(mMockPm, times(1)).getPackageInfo(
+ sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
}
/**
@@ -90,15 +107,18 @@ public class WatchdogRollbackLoggerTest {
@Test
public void testLogPackageHasParentKey() throws Exception {
Bundle bundle = new Bundle();
- bundle.putString(LOGGING_PARENT_KEY, "logging.parent");
+ bundle.putString(LOGGING_PARENT_KEY, LOGGING_PARENT_VALUE);
mApplicationInfo.metaData = bundle;
+ mPackageInfo.applicationInfo = mApplicationInfo;
mPackageInfo.setLongVersionCode(12345L);
- when(mMockPm.getApplicationInfo(anyString(), anyInt())).thenReturn(mApplicationInfo);
when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
sTestPackageV1);
- VersionedPackage expectedLogPackage = new VersionedPackage("logging.parent", 12345);
+ VersionedPackage expectedLogPackage = new VersionedPackage(LOGGING_PARENT_VALUE, 12345);
assertThat(logPackage).isEqualTo(expectedLogPackage);
+ verify(mMockPm, times(1)).getPackageInfo(
+ sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
+
}
/**
@@ -107,12 +127,64 @@ public class WatchdogRollbackLoggerTest {
@Test
public void testLogPackageNameNotFound() throws Exception {
Bundle bundle = new Bundle();
- bundle.putString("android.content.pm.LOGGING_PARENT", "logging.parent");
+ bundle.putString(LOGGING_PARENT_KEY, LOGGING_PARENT_VALUE);
mApplicationInfo.metaData = bundle;
- when(mMockPm.getPackageInfo(anyString(), anyInt())).thenThrow(
+ mPackageInfo.applicationInfo = mApplicationInfo;
+ when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
+ when(mMockPm.getPackageInfo(same(LOGGING_PARENT_VALUE), anyInt())).thenThrow(
new PackageManager.NameNotFoundException());
VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
sTestPackageV1);
assertThat(logPackage).isNull();
+ verify(mMockPm, times(1)).getPackageInfo(
+ sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
+ }
+
+ /**
+ * Ensures that we make the correct Package Manager calls in the case that the failing packages
+ * are correctly configured with parent packages.
+ */
+ @Test
+ public void testApexdLoggingCallsWithParents() throws Exception {
+ for (String failingPackage: sFailingPackages) {
+ PackageInfo packageInfo = new PackageInfo();
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ Bundle bundle = new Bundle();
+ bundle.putString(LOGGING_PARENT_KEY, getParent(failingPackage));
+ applicationInfo.metaData = bundle;
+ packageInfo.applicationInfo = applicationInfo;
+ when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
+ }
+
+ when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
+ WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
+ for (String failingPackage: sFailingPackages) {
+ verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
+ verify(mMockPm, times(1)).getPackageInfo(getParent(failingPackage), 0);
+ }
+ }
+
+ /**
+ * Ensures that we don't make any calls to parent packages in the case that packages are not
+ * correctly configured with parent packages.
+ */
+ @Test
+ public void testApexdLoggingCallsWithNoParents() throws Exception {
+ for (String failingPackage: sFailingPackages) {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
+ }
+ when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
+
+ WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
+ verify(mMockPm, times(sFailingPackages.size())).getPackageInfo(anyString(), anyInt());
+ for (String failingPackage: sFailingPackages) {
+ verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
+ }
+ }
+
+ private String getParent(String packageName) {
+ return packageName + "-parent";
}
}
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 8c4544249b5c..123bb079dbbb 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -37,6 +37,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.STATUS_BAR" />
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 7172a1b14244..f7aa3cc9e52c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -29,10 +29,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -45,8 +45,10 @@ import android.app.ActivityManager.StackInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
import android.view.Display;
import android.view.ITaskOrganizer;
import android.view.IWindowContainer;
@@ -357,6 +359,78 @@ public class TaskOrganizerTests extends WindowTestsBase {
assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
}
+ @Test
+ public void testHierarchyTransaction() {
+ final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>();
+ ITaskOrganizer listener = new ITaskOrganizer.Stub() {
+ @Override
+ public void taskAppeared(RunningTaskInfo taskInfo) { }
+
+ @Override
+ public void taskVanished(IWindowContainer container) { }
+
+ @Override
+ public void transactionReady(int id, SurfaceControl.Transaction t) { }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo info) {
+ lastReportedTiles.put(info.token.asBinder(), info);
+ }
+ };
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
+ listener, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+ final ActivityStack stack = createTaskStackOnDisplay(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final ActivityStack stack2 = createTaskStackOnDisplay(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
+
+ lastReportedTiles.clear();
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
+ wct.reparent(stack2.mRemoteToken, info2.token, true /* onTop */);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct,
+ null /* organizer */);
+ assertFalse(lastReportedTiles.isEmpty());
+ assertEquals(ACTIVITY_TYPE_STANDARD,
+ lastReportedTiles.get(info1.token.asBinder()).topActivityType);
+ assertEquals(ACTIVITY_TYPE_HOME,
+ lastReportedTiles.get(info2.token.asBinder()).topActivityType);
+
+ lastReportedTiles.clear();
+ wct = new WindowContainerTransaction();
+ wct.reparent(stack2.mRemoteToken, info1.token, false /* onTop */);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct,
+ null /* organizer */);
+ assertFalse(lastReportedTiles.isEmpty());
+ // Standard should still be on top of tile 1, so no change there
+ assertFalse(lastReportedTiles.containsKey(info1.token.asBinder()));
+ // But tile 2 has no children, so should become undefined
+ assertEquals(ACTIVITY_TYPE_UNDEFINED,
+ lastReportedTiles.get(info2.token.asBinder()).topActivityType);
+
+ // Check the getChildren call
+ List<RunningTaskInfo> children =
+ mWm.mAtmService.mTaskOrganizerController.getChildTasks(info1.token);
+ assertEquals(2, children.size());
+ children = mWm.mAtmService.mTaskOrganizerController.getChildTasks(info2.token);
+ assertEquals(0, children.size());
+
+ lastReportedTiles.clear();
+ wct = new WindowContainerTransaction();
+ wct.reorder(stack2.mRemoteToken, true /* onTop */);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct,
+ null /* organizer */);
+ // Home should now be on top. No change occurs in second tile, so not reported
+ assertEquals(1, lastReportedTiles.size());
+ assertEquals(ACTIVITY_TYPE_HOME,
+ lastReportedTiles.get(info1.token.asBinder()).topActivityType);
+ }
+
private List<TaskTile> getTaskTiles(DisplayContent dc) {
ArrayList<TaskTile> out = new ArrayList<>();
for (int i = dc.getStackCount() - 1; i >= 0; --i) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
new file mode 100644
index 000000000000..35723abb4310
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -0,0 +1,60 @@
+/*
+ * 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.wm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class WindowManagerServiceTests extends WindowTestsBase {
+ @Rule
+ public ExpectedException mExpectedException = ExpectedException.none();
+
+ @Test
+ public void testForceShowSystemBarsThrowsExceptionForNonAutomotive() {
+ if (!isAutomotive()) {
+ mExpectedException.expect(UnsupportedOperationException.class);
+
+ mWm.setForceShowSystemBars(true);
+ }
+ }
+
+ @Test
+ public void testForceShowSystemBarsDoesNotThrowExceptionForAutomotiveWithStatusBarPermission() {
+ if (isAutomotive()) {
+ mExpectedException.none();
+
+ mWm.setForceShowSystemBars(true);
+ }
+ }
+
+ private boolean isAutomotive() {
+ return getInstrumentation().getTargetContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/CharacterSets.java b/telephony/common/com/google/android/mms/pdu/CharacterSets.java
index 5172b7b67f88..a3894d6d2d34 100644
--- a/telephony/common/com/google/android/mms/pdu/CharacterSets.java
+++ b/telephony/common/com/google/android/mms/pdu/CharacterSets.java
@@ -48,6 +48,51 @@ public class CharacterSets {
public static final int UTF_16 = 0x03F7;
/**
+ * Extend charsets.
+ *
+ * From http://www.iana.org/assignments/character-sets/
+ */
+ public static final int BIG5_HKSCS = 0x0835; //2101
+ public static final int BOCU_1 = 0x03FC; //1020
+ public static final int CESU_8 = 0x03F8; //1016
+ public static final int CP864 = 0x0803; //2051
+ public static final int EUC_JP = 0x12; //18
+ public static final int EUC_KR = 0x26; //38
+ public static final int GB18030 = 0x72; //114
+ public static final int GBK = 0x71; //113
+ public static final int HZ_GB_2312 = 0x0825; //2085
+ public static final int GB_2312 = 0x07E9; //2025
+ public static final int ISO_2022_CN = 0x68; //104
+ public static final int ISO_2022_CN_EXT = 0x69; //105
+ public static final int ISO_2022_JP = 0x27; //39
+ public static final int ISO_2022_KR = 0x25; //37
+ public static final int ISO_8859_10 = 0x0D; //13
+ public static final int ISO_8859_13 = 0x6D; //109
+ public static final int ISO_8859_14 = 0x6E; //110
+ public static final int ISO_8859_15 = 0x6F; //111
+ public static final int ISO_8859_16 = 0x70; //112
+ public static final int KOI8_R = 0x0824; //2084
+ public static final int KOI8_U = 0x0828; //2088
+ public static final int MACINTOSH = 0x07EB; //2027
+ public static final int SCSU = 0x03F3; //1011
+ public static final int TIS_620 = 0x08D3; //2259
+ public static final int UTF_16BE = 0x03F5; //1013
+ public static final int UTF_16LE = 0x03F6; //1014
+ public static final int UTF_32 = 0x03F9; //1017
+ public static final int UTF_32BE = 0x03FA; //1018
+ public static final int UTF_32LE = 0x03FB; //1019
+ public static final int UTF_7 = 0x03F4; //1012
+ public static final int WINDOWS_1250 = 0x08CA; //2250
+ public static final int WINDOWS_1251 = 0x08CB; //2251
+ public static final int WINDOWS_1252 = 0x08CC; //2252
+ public static final int WINDOWS_1253 = 0x08CD; //2253
+ public static final int WINDOWS_1254 = 0x08CE; //2254
+ public static final int WINDOWS_1255 = 0x08CF; //2255
+ public static final int WINDOWS_1256 = 0x08D0; //2256
+ public static final int WINDOWS_1257 = 0x08D1; //2257
+ public static final int WINDOWS_1258 = 0x08D2; //2258
+
+ /**
* If the encoding of given data is unsupported, use UTF_8 to decode it.
*/
public static final int DEFAULT_CHARSET = UTF_8;
@@ -72,6 +117,45 @@ public class CharacterSets {
BIG5,
UCS2,
UTF_16,
+ BIG5_HKSCS,
+ BOCU_1,
+ CESU_8,
+ CP864,
+ EUC_JP,
+ EUC_KR,
+ GB18030,
+ GBK,
+ HZ_GB_2312,
+ GB_2312,
+ ISO_2022_CN,
+ ISO_2022_CN_EXT,
+ ISO_2022_JP,
+ ISO_2022_KR,
+ ISO_8859_10,
+ ISO_8859_13,
+ ISO_8859_14,
+ ISO_8859_15,
+ ISO_8859_16,
+ KOI8_R,
+ KOI8_U,
+ MACINTOSH,
+ SCSU,
+ TIS_620,
+ UTF_16BE,
+ UTF_16LE,
+ UTF_32,
+ UTF_32BE,
+ UTF_32LE,
+ UTF_7,
+ WINDOWS_1250,
+ WINDOWS_1251,
+ WINDOWS_1252,
+ WINDOWS_1253,
+ WINDOWS_1254,
+ WINDOWS_1255,
+ WINDOWS_1256,
+ WINDOWS_1257,
+ WINDOWS_1258,
};
/**
@@ -94,6 +178,51 @@ public class CharacterSets {
public static final String MIMENAME_UCS2 = "iso-10646-ucs-2";
public static final String MIMENAME_UTF_16 = "utf-16";
+ /**
+ * Extend charsets.
+ *
+ * From http://www.iana.org/assignments/character-sets/
+ */
+ public static final String MIMENAME_BIG5_HKSCS = "Big5-HKSCS";
+ public static final String MIMENAME_BOCU_1 = "BOCU-1";
+ public static final String MIMENAME_CESU_8 = "CESU-8";
+ public static final String MIMENAME_CP864 = "cp864";
+ public static final String MIMENAME_EUC_JP = "EUC-JP";
+ public static final String MIMENAME_EUC_KR = "EUC-KR";
+ public static final String MIMENAME_GB18030 = "GB18030";
+ public static final String MIMENAME_GBK = "GBK";
+ public static final String MIMENAME_HZ_GB_2312 = "HZ-GB-2312";
+ public static final String MIMENAME_GB_2312 = "GB2312";
+ public static final String MIMENAME_ISO_2022_CN = "ISO-2022-CN";
+ public static final String MIMENAME_ISO_2022_CN_EXT = "ISO-2022-CN-EXT";
+ public static final String MIMENAME_ISO_2022_JP = "ISO-2022-JP";
+ public static final String MIMENAME_ISO_2022_KR = "ISO-2022-KR";
+ public static final String MIMENAME_ISO_8859_10 = "ISO-8859-10";
+ public static final String MIMENAME_ISO_8859_13 = "ISO-8859-13";
+ public static final String MIMENAME_ISO_8859_14 = "ISO-8859-14";
+ public static final String MIMENAME_ISO_8859_15 = "ISO-8859-15";
+ public static final String MIMENAME_ISO_8859_16 = "ISO-8859-16";
+ public static final String MIMENAME_KOI8_R = "KOI8-R";
+ public static final String MIMENAME_KOI8_U = "KOI8-U";
+ public static final String MIMENAME_MACINTOSH = "macintosh";
+ public static final String MIMENAME_SCSU = "SCSU";
+ public static final String MIMENAME_TIS_620 = "TIS-620";
+ public static final String MIMENAME_UTF_16BE = "UTF-16BE";
+ public static final String MIMENAME_UTF_16LE = "UTF-16LE";
+ public static final String MIMENAME_UTF_32 = "UTF-32";
+ public static final String MIMENAME_UTF_32BE = "UTF-32BE";
+ public static final String MIMENAME_UTF_32LE = "UTF-32LE";
+ public static final String MIMENAME_UTF_7 = "UTF-7";
+ public static final String MIMENAME_WINDOWS_1250 = "windows-1250";
+ public static final String MIMENAME_WINDOWS_1251 = "windows-1251";
+ public static final String MIMENAME_WINDOWS_1252 = "windows-1252";
+ public static final String MIMENAME_WINDOWS_1253 = "windows-1253";
+ public static final String MIMENAME_WINDOWS_1254 = "windows-1254";
+ public static final String MIMENAME_WINDOWS_1255 = "windows-1255";
+ public static final String MIMENAME_WINDOWS_1256 = "windows-1256";
+ public static final String MIMENAME_WINDOWS_1257 = "windows-1257";
+ public static final String MIMENAME_WINDOWS_1258 = "windows-1258";
+
public static final String DEFAULT_CHARSET_NAME = MIMENAME_UTF_8;
/**
@@ -116,6 +245,45 @@ public class CharacterSets {
MIMENAME_BIG5,
MIMENAME_UCS2,
MIMENAME_UTF_16,
+ MIMENAME_BIG5_HKSCS,
+ MIMENAME_BOCU_1,
+ MIMENAME_CESU_8,
+ MIMENAME_CP864,
+ MIMENAME_EUC_JP,
+ MIMENAME_EUC_KR,
+ MIMENAME_GB18030,
+ MIMENAME_GBK,
+ MIMENAME_HZ_GB_2312,
+ MIMENAME_GB_2312,
+ MIMENAME_ISO_2022_CN,
+ MIMENAME_ISO_2022_CN_EXT,
+ MIMENAME_ISO_2022_JP,
+ MIMENAME_ISO_2022_KR,
+ MIMENAME_ISO_8859_10,
+ MIMENAME_ISO_8859_13,
+ MIMENAME_ISO_8859_14,
+ MIMENAME_ISO_8859_15,
+ MIMENAME_ISO_8859_16,
+ MIMENAME_KOI8_R,
+ MIMENAME_KOI8_U,
+ MIMENAME_MACINTOSH,
+ MIMENAME_SCSU,
+ MIMENAME_TIS_620,
+ MIMENAME_UTF_16BE,
+ MIMENAME_UTF_16LE,
+ MIMENAME_UTF_32,
+ MIMENAME_UTF_32BE,
+ MIMENAME_UTF_32LE,
+ MIMENAME_UTF_7,
+ MIMENAME_WINDOWS_1250,
+ MIMENAME_WINDOWS_1251,
+ MIMENAME_WINDOWS_1252,
+ MIMENAME_WINDOWS_1253,
+ MIMENAME_WINDOWS_1254,
+ MIMENAME_WINDOWS_1255,
+ MIMENAME_WINDOWS_1256,
+ MIMENAME_WINDOWS_1257,
+ MIMENAME_WINDOWS_1258,
};
private static final HashMap<Integer, String> MIBENUM_TO_NAME_MAP;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e3d031dfa476..e520a02c2721 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7761,9 +7761,8 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
public static final int DEFAULT_PREFERRED_NETWORK_MODE =
- RILConstants.DEFAULT_PREFERRED_NETWORK_MODE;
+ RILConstants.PREFERRED_NETWORK_MODE;
/**
* Get the preferred network type.
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 9ac8cb136c6b..c40573b25068 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -233,14 +233,11 @@ public interface RILConstants {
/** NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA */
int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 33;
- /** Default preferred network mode */
- int DEFAULT_PREFERRED_NETWORK_MODE = NETWORK_MODE_WCDMA_PREF;
-
@UnsupportedAppUsage
int PREFERRED_NETWORK_MODE = Optional.of(TelephonyProperties.default_network())
.filter(list -> !list.isEmpty())
.map(list -> list.get(0))
- .orElse(DEFAULT_PREFERRED_NETWORK_MODE);
+ .orElse(NETWORK_MODE_WCDMA_PREF);
int BAND_MODE_UNSPECIFIED = 0; //"unspecified" (selected by baseband automatically)
int BAND_MODE_EURO = 1; //"EURO band" (GSM-900 / DCS-1800 / WCDMA-IMT-2000)
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 6a29b1c36052..1763975e7770 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -47,7 +47,7 @@ filegroup {
// framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
// to a separate package.
"java/android/net/wifi/WifiNetworkScoreCache.java",
- "java/android/net/wifi/WifiOemConfigStoreMigrationHook.java",
+ "java/android/net/wifi/WifiOemMigrationHook.java",
"java/android/net/wifi/wificond/*.java",
":libwificond_ipc_aidl",
],
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1f1c0c12cf21..221f64440969 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -266,4 +266,8 @@ interface IWifiManager
* Return the Map of {@link WifiNetworkSuggestion} and the list of <ScanResult>
*/
Map getMatchingScanResults(in List<WifiNetworkSuggestion> networkSuggestions, in List<ScanResult> scanResults, String callingPackage, String callingFeatureId);
+
+ void setScanThrottleEnabled(boolean enable);
+
+ boolean isScanThrottleEnabled();
}
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 7c031eaaeaf4..24b2a8e8994f 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -449,9 +449,8 @@ public class WifiInfo implements Parcelable {
* <p>
* If the SSID can be decoded as UTF-8, it will be returned surrounded by double
* quotation marks. Otherwise, it is returned as a string of hex digits.
- * The SSID may be
- * <lt>&lt;unknown ssid&gt;, if there is no network currently connected or if the caller has
- * insufficient permissions to access the SSID.<lt>
+ * The SSID may be {@link WifiManager#UNKNOWN_SSID}, if there is no network currently connected
+ * or if the caller has insufficient permissions to access the SSID.
* </p>
* <p>
* Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index a3cce7c00b3a..5ccc3aa429c6 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2982,7 +2982,7 @@ public class WifiManager {
}
/**
- * Start Soft AP (hotspot) mode with the specified configuration.
+ * Start Soft AP (hotspot) mode for tethering purposes with the specified configuration.
* Note that starting Soft AP mode may disable station mode operation if the device does not
* support concurrency.
* @param wifiConfig SSID, security and channel details as part of WifiConfiguration, or null to
@@ -3278,7 +3278,7 @@ public class WifiManager {
}
/**
- * Gets the Wi-Fi enabled state.
+ * Gets the tethered Wi-Fi hotspot enabled state.
* @return One of {@link #WIFI_AP_STATE_DISABLED},
* {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
* {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
@@ -3297,8 +3297,8 @@ public class WifiManager {
}
/**
- * Return whether Wi-Fi AP is enabled or disabled.
- * @return {@code true} if Wi-Fi AP is enabled
+ * Return whether tethered Wi-Fi AP is enabled or disabled.
+ * @return {@code true} if tethered Wi-Fi AP is enabled
* @see #getWifiApState()
*
* @hide
@@ -3310,7 +3310,7 @@ public class WifiManager {
}
/**
- * Gets the Wi-Fi AP Configuration.
+ * Gets the tethered Wi-Fi AP Configuration.
* @return AP details in WifiConfiguration
*
* Note that AP detail may contain configuration which is cannot be represented
@@ -3332,7 +3332,7 @@ public class WifiManager {
}
/**
- * Gets the Wi-Fi AP Configuration.
+ * Gets the Wi-Fi tethered AP Configuration.
* @return AP details in {@link SoftApConfiguration}
*
* @hide
@@ -3349,7 +3349,7 @@ public class WifiManager {
}
/**
- * Sets the Wi-Fi AP Configuration.
+ * Sets the tethered Wi-Fi AP Configuration.
* @return {@code true} if the operation succeeded, {@code false} otherwise
*
* @deprecated This API is deprecated. Use {@link #setSoftApConfiguration(SoftApConfiguration)}
@@ -3368,9 +3368,9 @@ public class WifiManager {
}
/**
- * Sets the Wi-Fi AP Configuration.
+ * Sets the tethered Wi-Fi AP Configuration.
*
- * If the API is called while the soft AP is enabled, the configuration will apply to
+ * If the API is called while the tethered soft AP is enabled, the configuration will apply to
* the current soft AP if the new configuration only includes
* {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)}
* or {@link SoftApConfiguration.Builder#setShutdownTimeoutMillis(int)}
@@ -6118,4 +6118,48 @@ public class WifiManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Enable/disable wifi scan throttling from 3rd party apps.
+ *
+ * <p>
+ * The throttling limits for apps are described in
+ * <a href="Wi-Fi Scan Throttling">
+ * https://developer.android.com/guide/topics/connectivity/wifi-scan#wifi-scan-throttling</a>
+ * </p>
+ *
+ * @param enable true to allow scan throttling, false to disallow scan throttling.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void setScanThrottleEnabled(boolean enable) {
+ try {
+ mService.setScanThrottleEnabled(enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the persisted Wi-Fi scan throttle state. Defaults to true, unless changed by the user via
+ * Developer options.
+ *
+ * <p>
+ * The throttling limits for apps are described in
+ * <a href="Wi-Fi Scan Throttling">
+ * https://developer.android.com/guide/topics/connectivity/wifi-scan#wifi-scan-throttling</a>
+ * </p>
+ *
+ * @return true to indicate that scan throttling is enabled, false to indicate that scan
+ * throttling is disabled.
+ */
+ @RequiresPermission(ACCESS_WIFI_STATE)
+ public boolean isScanThrottleEnabled() {
+ try {
+ return mService.isScanThrottleEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
index 642dcb9211ae..22d778637101 100755
--- a/wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java
+++ b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
@@ -28,30 +28,17 @@ import java.util.List;
/**
* Class used to provide one time hooks for existing OEM devices to migrate their config store
- * data to the wifi mainline module.
- * <p>
- * Note:
- * <li> OEM's need to implement {@link #load()} only if their
- * existing config store format or file locations differs from the vanilla AOSP implementation (
- * which is what the wifi mainline module understands).
- * </li>
- * <li> The wifi mainline module will invoke {@link #load()} method on every bootup, its
- * the responsibility of the OEM implementation to ensure that this method returns non-null data
- * only on the first bootup. Once the migration is done, the OEM can safely delete their config
- * store files and then return null on any subsequent reboots. The first & only relevant invocation
- * of {@link #load()} occurs when a previously released device upgrades to the wifi
- * mainline module from an OEM implementation of the wifi stack.
- * </li>
+ * data and other settings to the wifi mainline module.
* @hide
*/
@SystemApi
-public final class WifiOemConfigStoreMigrationHook {
+public final class WifiOemMigrationHook {
/**
* Container for all the wifi config data to migrate.
*/
- public static final class MigrationData implements Parcelable {
+ public static final class ConfigStoreMigrationData implements Parcelable {
/**
- * Builder to create instance of {@link MigrationData}.
+ * Builder to create instance of {@link ConfigStoreMigrationData}.
*/
public static final class Builder {
private List<WifiConfiguration> mUserSavedNetworkConfigurations;
@@ -92,39 +79,40 @@ public final class WifiOemConfigStoreMigrationHook {
}
/**
- * Build an instance of {@link MigrationData}.
+ * Build an instance of {@link ConfigStoreMigrationData}.
*
- * @return Instance of {@link MigrationData}.
+ * @return Instance of {@link ConfigStoreMigrationData}.
*/
- public @NonNull MigrationData build() {
- return new MigrationData(mUserSavedNetworkConfigurations, mUserSoftApConfiguration);
+ public @NonNull ConfigStoreMigrationData build() {
+ return new ConfigStoreMigrationData(
+ mUserSavedNetworkConfigurations, mUserSoftApConfiguration);
}
}
private final List<WifiConfiguration> mUserSavedNetworkConfigurations;
private final SoftApConfiguration mUserSoftApConfiguration;
- private MigrationData(
+ private ConfigStoreMigrationData(
@Nullable List<WifiConfiguration> userSavedNetworkConfigurations,
@Nullable SoftApConfiguration userSoftApConfiguration) {
mUserSavedNetworkConfigurations = userSavedNetworkConfigurations;
mUserSoftApConfiguration = userSoftApConfiguration;
}
- public static final @NonNull Parcelable.Creator<MigrationData> CREATOR =
- new Parcelable.Creator<MigrationData>() {
+ public static final @NonNull Parcelable.Creator<ConfigStoreMigrationData> CREATOR =
+ new Parcelable.Creator<ConfigStoreMigrationData>() {
@Override
- public MigrationData createFromParcel(Parcel in) {
+ public ConfigStoreMigrationData createFromParcel(Parcel in) {
List<WifiConfiguration> userSavedNetworkConfigurations =
in.readArrayList(null);
SoftApConfiguration userSoftApConfiguration = in.readParcelable(null);
- return new MigrationData(
+ return new ConfigStoreMigrationData(
userSavedNetworkConfigurations, userSoftApConfiguration);
}
@Override
- public MigrationData[] newArray(int size) {
- return new MigrationData[size];
+ public ConfigStoreMigrationData[] newArray(int size) {
+ return new ConfigStoreMigrationData[size];
}
};
@@ -164,16 +152,29 @@ public final class WifiOemConfigStoreMigrationHook {
}
}
- private WifiOemConfigStoreMigrationHook() { }
+ private WifiOemMigrationHook() { }
/**
* Load data from OEM's config store.
+ * <p>
+ * Note:
+ * <li> OEM's need to implement {@link #loadFromConfigStore()} ()} only if their
+ * existing config store format or file locations differs from the vanilla AOSP implementation (
+ * which is what the wifi mainline module understands).
+ * </li>
+ * <li> The wifi mainline module will invoke {@link #loadFromConfigStore()} method on every
+ * bootup, its the responsibility of the OEM implementation to ensure that this method returns
+ * non-null data only on the first bootup. Once the migration is done, the OEM can safely delete
+ * their config store files and then return null on any subsequent reboots. The first & only
+ * relevant invocation of {@link #loadFromConfigStore()} occurs when a previously released
+ * device upgrades to the wifi mainline module from an OEM implementation of the wifi stack.
+ * </li>
*
- * @return Instance of {@link MigrationData} for migrating data, null if no
+ * @return Instance of {@link ConfigStoreMigrationData} for migrating data, null if no
* migration is necessary.
*/
@Nullable
- public static MigrationData load() {
+ public static ConfigStoreMigrationData loadFromConfigStore() {
// Note: OEM's should add code to parse data from their config store format here!
return null;
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 36c7213b3799..d47989235f0b 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -17,6 +17,7 @@
package android.net.wifi.p2p;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -133,6 +134,7 @@ public class WifiP2pConfig implements Parcelable {
*
* By default this field is set to {@link #GROUP_OWNER_INTENT_AUTO}.
*/
+ @IntRange(from = 0, to = 15)
public int groupOwnerIntent = GROUP_OWNER_INTENT_AUTO;
/** @hide */
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
index cdb2806af0b1..8a86311defca 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
@@ -22,7 +22,9 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.LruCache;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
@@ -78,8 +80,8 @@ public final class WifiP2pGroupList implements Parcelable {
* Get the list of P2P groups.
*/
@NonNull
- public Collection<WifiP2pGroup> getGroupList() {
- return mGroups.snapshot().values();
+ public List<WifiP2pGroup> getGroupList() {
+ return new ArrayList<>(mGroups.snapshot().values());
}
/**
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 0fe06756a969..9c2cad99614d 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -326,6 +326,12 @@ public class WifiP2pManager {
/**
* Broadcast intent action indicating that remembered persistent groups have changed.
+ *
+ * You can <em>not</em> receive this through components declared
+ * in manifests, only by explicitly registering for it with
+ * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver,
+ * android.content.IntentFilter) Context.registerReceiver()}.
+ *
* @hide
*/
@SystemApi
@@ -1347,20 +1353,33 @@ public class WifiP2pManager {
}
/**
- * Force p2p to enter or exit listen state
+ * Force p2p to enter listen state
*
* @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}
- * @param enable enables or disables listening
* @param listener for callbacks on success or failure. Can be null.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public void listen(@NonNull Channel c, boolean enable, @Nullable ActionListener listener) {
+ public void startListening(@NonNull Channel c, @Nullable ActionListener listener) {
checkChannel(c);
- c.mAsyncChannel.sendMessage(enable ? START_LISTEN : STOP_LISTEN,
- 0, c.putListener(listener));
+ c.mAsyncChannel.sendMessage(START_LISTEN, 0, c.putListener(listener));
+ }
+
+ /**
+ * Force p2p to exit listen state
+ *
+ * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}
+ * @param listener for callbacks on success or failure. Can be null.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void stopListening(@NonNull Channel c, @Nullable ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(STOP_LISTEN, 0, c.putListener(listener));
}
/**
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
index 5484d248429f..e399b5b9afa6 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
@@ -17,6 +17,7 @@
package android.net.wifi.p2p;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -34,7 +35,7 @@ import java.util.Locale;
*/
public final class WifiP2pWfdInfo implements Parcelable {
- private boolean mWfdEnabled;
+ private boolean mEnabled;
/** Device information bitmap */
private int mDeviceInfo;
@@ -85,15 +86,15 @@ public final class WifiP2pWfdInfo implements Parcelable {
/** @hide */
@UnsupportedAppUsage
public WifiP2pWfdInfo(int devInfo, int ctrlPort, int maxTput) {
- mWfdEnabled = true;
+ mEnabled = true;
mDeviceInfo = devInfo;
mCtrlPort = ctrlPort;
mMaxThroughput = maxTput;
}
/** Returns true is Wifi Display is enabled, false otherwise. */
- public boolean isWfdEnabled() {
- return mWfdEnabled;
+ public boolean isEnabled() {
+ return mEnabled;
}
/**
@@ -101,8 +102,8 @@ public final class WifiP2pWfdInfo implements Parcelable {
*
* @param enabled true to enable Wifi Display, false to disable
*/
- public void setWfdEnabled(boolean enabled) {
- mWfdEnabled = enabled;
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
}
/**
@@ -177,12 +178,12 @@ public final class WifiP2pWfdInfo implements Parcelable {
}
/** Sets the TCP port at which the WFD Device listens for RTSP messages. */
- public void setControlPort(int port) {
+ public void setControlPort(@IntRange(from = 0) int port) {
mCtrlPort = port;
}
/** Sets the maximum average throughput capability of the WFD Device, in megabits/second. */
- public void setMaxThroughput(int maxThroughput) {
+ public void setMaxThroughput(@IntRange(from = 0) int maxThroughput) {
mMaxThroughput = maxThroughput;
}
@@ -200,7 +201,7 @@ public final class WifiP2pWfdInfo implements Parcelable {
@Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
- sbuf.append("WFD enabled: ").append(mWfdEnabled);
+ sbuf.append("WFD enabled: ").append(mEnabled);
sbuf.append("WFD DeviceInfo: ").append(mDeviceInfo);
sbuf.append("\n WFD CtrlPort: ").append(mCtrlPort);
sbuf.append("\n WFD MaxThroughput: ").append(mMaxThroughput);
@@ -215,7 +216,7 @@ public final class WifiP2pWfdInfo implements Parcelable {
/** Copy constructor. */
public WifiP2pWfdInfo(@Nullable WifiP2pWfdInfo source) {
if (source != null) {
- mWfdEnabled = source.mWfdEnabled;
+ mEnabled = source.mEnabled;
mDeviceInfo = source.mDeviceInfo;
mCtrlPort = source.mCtrlPort;
mMaxThroughput = source.mMaxThroughput;
@@ -225,14 +226,14 @@ public final class WifiP2pWfdInfo implements Parcelable {
/** Implement the Parcelable interface */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mWfdEnabled ? 1 : 0);
+ dest.writeInt(mEnabled ? 1 : 0);
dest.writeInt(mDeviceInfo);
dest.writeInt(mCtrlPort);
dest.writeInt(mMaxThroughput);
}
private void readFromParcel(Parcel in) {
- mWfdEnabled = (in.readInt() == 1);
+ mEnabled = (in.readInt() == 1);
mDeviceInfo = in.readInt();
mCtrlPort = in.readInt();
mMaxThroughput = in.readInt();
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
deleted file mode 100644
index 060c85cac209..000000000000
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ /dev/null
@@ -1,641 +0,0 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License") {
- * throw new UnsupportedOperationException();
- }
- * 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.wifi;
-
-import android.content.pm.ParceledListSlice;
-import android.net.DhcpInfo;
-import android.net.Network;
-import android.net.wifi.IActionListener;
-import android.net.wifi.IDppCallback;
-import android.net.wifi.ILocalOnlyHotspotCallback;
-import android.net.wifi.INetworkRequestMatchCallback;
-import android.net.wifi.IOnWifiActivityEnergyInfoListener;
-import android.net.wifi.IOnWifiUsabilityStatsListener;
-import android.net.wifi.IScanResultsCallback;
-import android.net.wifi.ISoftApCallback;
-import android.net.wifi.ISuggestionConnectionStatusListener;
-import android.net.wifi.ITrafficStateCallback;
-import android.net.wifi.ITxPacketCountListener;
-import android.net.wifi.IWifiConnectedNetworkScorer;
-import android.net.wifi.IWifiManager;
-import android.net.wifi.ScanResult;
-import android.net.wifi.SoftApConfiguration;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.net.wifi.WifiNetworkSuggestion;
-import android.net.wifi.hotspot2.IProvisioningCallback;
-import android.net.wifi.hotspot2.OsuProvider;
-import android.net.wifi.hotspot2.PasspointConfiguration;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.WorkSource;
-import android.os.connectivity.WifiActivityEnergyInfo;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Empty concrete class implementing IWifiManager with stub methods throwing runtime exceptions.
- *
- * This class is meant to be extended by real implementations of IWifiManager in order to facilitate
- * cross-repo changes to WiFi internal APIs, including the introduction of new APIs, the removal of
- * deprecated APIs, or the migration of existing API signatures.
- *
- * When an existing API is scheduled for removal, it can be removed from IWifiManager.aidl
- * immediately and marked as @Deprecated first in this class. Children inheriting this class are
- * then given a short grace period to update themselves before the @Deprecated stub is removed for
- * good. If the API scheduled for removal has a replacement or an overload (signature change),
- * these should be introduced before the stub is removed to allow children to migrate.
- *
- * When a new API is added to IWifiManager.aidl, a stub should be added in BaseWifiService as
- * well otherwise compilation will fail.
- */
-public class BaseWifiService extends IWifiManager.Stub {
-
- private static final String TAG = BaseWifiService.class.getSimpleName();
-
- @Override
- public long getSupportedFeatures() {
- throw new UnsupportedOperationException();
- }
-
- /** @deprecated use {@link #getWifiActivityEnergyInfoAsync} instead */
- @Deprecated
- public WifiActivityEnergyInfo reportActivityInfo() {
- throw new UnsupportedOperationException();
- }
-
- /** @deprecated use {@link #getWifiActivityEnergyInfoAsync} instead */
- @Deprecated
- public void requestActivityInfo(ResultReceiver result) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void getWifiActivityEnergyInfoAsync(IOnWifiActivityEnergyInfoListener listener) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public ParceledListSlice getConfiguredNetworks(String packageName, String featureId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public ParceledListSlice getPrivilegedConfiguredNetworks(String packageName, String featureId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Map<String, Map<Integer, List<ScanResult>>> getAllMatchingFqdnsForScanResults(
- List<ScanResult> scanResults) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
- List<ScanResult> scanResults) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
- List<OsuProvider> osuProviders) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int addOrUpdateNetwork(WifiConfiguration config, String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean addOrUpdatePasspointConfiguration(
- PasspointConfiguration config, String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean removePasspointConfiguration(String fqdn, String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public List<PasspointConfiguration> getPasspointConfigurations(String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public List<WifiConfiguration> getWifiConfigsForPasspointProfiles(List<String> fqdnList) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void queryPasspointIcon(long bssid, String fileName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int matchProviderWithCurrentNetwork(String fqdn) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void deauthenticateNetwork(long holdoff, boolean ess) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean removeNetwork(int netId, String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean enableNetwork(int netId, boolean disableOthers, String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean disableNetwork(int netId, String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void allowAutojoinGlobal(boolean choice) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void allowAutojoin(int netId, boolean choice) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setMeteredOverridePasspoint(String fqdn, int meteredOverride) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean startScan(String packageName, String featureId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public List<ScanResult> getScanResults(String callingPackage, String callingFeatureId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean disconnect(String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean reconnect(String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean reassociate(String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public WifiInfo getConnectionInfo(String callingPackage, String callingFeatureId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean setWifiEnabled(String packageName, boolean enable) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getWifiEnabledState() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getCountryCode() {
- throw new UnsupportedOperationException();
- }
-
- /** @deprecated use {@link #is5GHzBandSupported} instead */
- @Deprecated
- public boolean isDualBandSupported() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean is5GHzBandSupported() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean is6GHzBandSupported() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isWifiStandardSupported(int standard) {
- throw new UnsupportedOperationException();
- }
-
- /** @deprecated use {@link WifiManager#isStaApConcurrencySupported()} */
- @Deprecated
- public boolean needs5GHzToAnyApBandConversion() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public DhcpInfo getDhcpInfo() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isScanAlwaysAvailable() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean acquireWifiLock(IBinder lock, int lockType, String tag, WorkSource ws) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean releaseWifiLock(IBinder lock) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void initializeMulticastFiltering() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isMulticastEnabled() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void acquireMulticastLock(IBinder binder, String tag) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void releaseMulticastLock(String tag) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void updateInterfaceIpState(String ifaceName, int mode) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean startSoftAp(WifiConfiguration wifiConfig) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean startTetheredHotspot(SoftApConfiguration softApConfig) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean stopSoftAp() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName,
- String featureId, SoftApConfiguration customConfig) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void stopLocalOnlyHotspot() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void startWatchLocalOnlyHotspot(ILocalOnlyHotspotCallback callback) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void stopWatchLocalOnlyHotspot() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getWifiApEnabledState() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public WifiConfiguration getWifiApConfiguration() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public SoftApConfiguration getSoftApConfiguration() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean setWifiApConfiguration(WifiConfiguration wifiConfig, String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean setSoftApConfiguration(SoftApConfiguration softApConfig, String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void notifyUserOfApBandConversion(String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void enableTdls(String remoteIPAddress, boolean enable) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getCurrentNetworkWpsNfcConfigurationToken() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void enableVerboseLogging(int verbose) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getVerboseLoggingLevel() {
- throw new UnsupportedOperationException();
- }
-
- /** @deprecated use {@link #allowAutojoinGlobal(boolean)} instead */
- @Deprecated
- public void enableWifiConnectivityManager(boolean enabled) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void disableEphemeralNetwork(String SSID, String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void factoryReset(String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Network getCurrentNetwork() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public byte[] retrieveBackupData() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void restoreBackupData(byte[] data) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public byte[] retrieveSoftApBackupData() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public SoftApConfiguration restoreSoftApBackupData(byte[] data) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void startSubscriptionProvisioning(
- OsuProvider provider, IProvisioningCallback callback) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void registerSoftApCallback(
- IBinder binder, ISoftApCallback callback, int callbackIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void unregisterSoftApCallback(int callbackIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void registerTrafficStateCallback(
- IBinder binder, ITrafficStateCallback callback, int callbackIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void unregisterTrafficStateCallback(int callbackIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void registerNetworkRequestMatchCallback(
- IBinder binder, INetworkRequestMatchCallback callback, int callbackIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void unregisterNetworkRequestMatchCallback(int callbackIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int addNetworkSuggestions(
- List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName,
- String callingFeatureId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int removeNetworkSuggestions(
- List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public List<WifiNetworkSuggestion> getNetworkSuggestions(String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String[] getFactoryMacAddresses() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setDeviceMobilityState(int state) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void startDppAsConfiguratorInitiator(IBinder binder, String enrolleeUri,
- int selectedNetworkId, int netRole, IDppCallback callback) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void startDppAsEnrolleeInitiator(IBinder binder, String configuratorUri,
- IDppCallback callback) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void stopDppSession() throws RemoteException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void addOnWifiUsabilityStatsListener(
- IBinder binder, IOnWifiUsabilityStatsListener listener, int listenerIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void removeOnWifiUsabilityStatsListener(int listenerIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void connect(WifiConfiguration config, int netId, IBinder binder,
- IActionListener callback, int callbackIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void save(WifiConfiguration config, IBinder binder, IActionListener callback,
- int callbackIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void forget(int netId, IBinder binder, IActionListener callback,
- int callbackIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void getTxPacketCount(String packageName, IBinder binder,
- ITxPacketCountListener callback, int callbackIdentifier) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void registerScanResultsCallback(IScanResultsCallback callback) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void unregisterScanResultsCallback(IScanResultsCallback callback) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void registerSuggestionConnectionStatusListener(IBinder binder,
- ISuggestionConnectionStatusListener listener,
- int listenerIdentifier, String packageName, String featureId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void unregisterSuggestionConnectionStatusListener(int listenerIdentifier,
- String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int calculateSignalLevel(int rssi) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(
- List<ScanResult> scanResults) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean setWifiConnectedNetworkScorer(IBinder binder,
- IWifiConnectedNetworkScorer scorer) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void clearWifiConnectedNetworkScorer() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults(
- List<WifiNetworkSuggestion> networkSuggestions,
- List<ScanResult> scanResults,
- String callingPackage, String callingFeatureId) {
- throw new UnsupportedOperationException();
- }
-}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 6320f85eb974..53e9755eaf52 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -2380,4 +2380,14 @@ public class WifiManagerTest {
verify(mWifiConnectedNetworkScorer).start(0);
verify(mWifiConnectedNetworkScorer).stop(10);
}
+
+ @Test
+ public void testScanThrottle() throws Exception {
+ mWifiManager.setScanThrottleEnabled(true);
+ verify(mWifiService).setScanThrottleEnabled(true);
+
+ when(mWifiService.isScanThrottleEnabled()).thenReturn(false);
+ assertFalse(mWifiManager.isScanThrottleEnabled());
+ verify(mWifiService).isScanThrottleEnabled();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
index 17ee75594c2f..6edc287068e8 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
@@ -45,7 +45,7 @@ public class WifiP2pDeviceTest {
assertEquals(devA.groupCapability, devB.groupCapability);
assertEquals(devA.status, devB.status);
if (devA.wfdInfo != null) {
- assertEquals(devA.wfdInfo.isWfdEnabled(), devB.wfdInfo.isWfdEnabled());
+ assertEquals(devA.wfdInfo.isEnabled(), devB.wfdInfo.isEnabled());
assertEquals(devA.wfdInfo.getDeviceInfoHex(), devB.wfdInfo.getDeviceInfoHex());
assertEquals(devA.wfdInfo.getControlPort(), devB.wfdInfo.getControlPort());
assertEquals(devA.wfdInfo.getMaxThroughput(), devB.wfdInfo.getMaxThroughput());
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java
index 15a0aacf6e5b..2a9b36b47172 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java
@@ -55,8 +55,8 @@ public class WifiP2pWfdInfoTest {
public void testSettersGetters() throws Exception {
WifiP2pWfdInfo info = new WifiP2pWfdInfo();
- info.setWfdEnabled(true);
- assertTrue(info.isWfdEnabled());
+ info.setEnabled(true);
+ assertTrue(info.isEnabled());
info.setDeviceType(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE);
assertEquals(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE, info.getDeviceType());